Skip to content

Commit

Permalink
Merge pull request #152 from schaumb/get_name
Browse files Browse the repository at this point in the history
Addressing the limitation of get_name
  • Loading branch information
apolukhin committed Jan 28, 2024
2 parents e969c57 + 16a97bb commit 4c15f21
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 12 deletions.
4 changes: 1 addition & 3 deletions doc/pfr.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -490,9 +490,7 @@ struct aggregate : empty { // not a SimpleAggregate
```
The library may work with aggregates that don't satisfy the requirements of `SimpleAggregate`, but the behavior tends to be non-portable.

Boost.PFRs extraction of field name works with a `SimpleAggregate` with non-internal linkage (with aggregats that could be used as `extern T t;`).
Do not use this functionality with anonymous structures, local structures
or a structure defined inside anonymous namespace as the behavior tends to be non-portable.
Boost.PFRs extraction of field name works with only `SimpleAggregate` types.


[h2 Configuration Macro]
Expand Down
5 changes: 1 addition & 4 deletions example/quick_examples.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,8 @@ void test_examples() {
//]
}

// Disabled from testing since it's unportable
#if 0
#if BOOST_PFR_CORE_NAME_ENABLED
{
// Keep in mind that it's unportable code
// You should move this structure somewhere outside of function scope
//[pfr_quick_examples_get_name
// Get name of field by index

Expand Down
17 changes: 12 additions & 5 deletions include/boost/pfr/detail/fake_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,25 @@

namespace boost { namespace pfr { namespace detail {

// This variable serves as a compile-time assert. If you see any error here, then
// you're probably using `boost::pfr::get_name()` or `boost::pfr::names_as_array()` with a non-external linkage type.
// This class has external linkage while T has not sure.
template <class T>
extern const T passed_type_has_no_external_linkage;
struct wrapper {
const T value;
};

// This variable servers as a link-time assert.
// If linker requires it, then `fake_object()` is used at runtime.
template <class T>
extern const wrapper<T> report_if_you_see_link_error_with_this_object;

// For returning non default constructible types, it's exclusively used in member name retrieval.
//
// Neither std::declval nor boost::pfr::detail::unsafe_declval are usable there.
// Limitation - T should have external linkage.
// This takes advantage of C++20 features, while boost::pfr::detail::unsafe_declval works
// with the former standards.
template <class T>
constexpr const T& fake_object() noexcept {
return passed_type_has_no_external_linkage<T>;
return report_if_you_see_link_error_with_this_object<T>.value;
}

}}} // namespace boost::pfr::detail
Expand Down
48 changes: 48 additions & 0 deletions test/core_name/run/fields_names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ struct A {

struct empty {};

namespace {
struct inside_unnamed_ns {
int hidden;
};
}

struct {
int unnamed_first;
float unnamed_second;
} unnamed{};

typedef typename std::remove_reference<decltype(unnamed)>::type unnamed_t;

void test_get_name_by_id() {
BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate>())), "member1");
BOOST_TEST_EQ( ((boost::pfr::get_name<1, Aggregate>())), "this_is_a_name");
Expand All @@ -44,6 +57,19 @@ void test_get_name_by_id() {
BOOST_TEST_EQ( ((boost::pfr::get_name<1, A>())), "second");
}

void test_get_name_by_id_without_linkage() {
struct function_local {
int val;
};

BOOST_TEST_EQ( ((boost::pfr::get_name<0, unnamed_t>())), "unnamed_first");
BOOST_TEST_EQ( ((boost::pfr::get_name<1, unnamed_t>())), "unnamed_second");

BOOST_TEST_EQ( ((boost::pfr::get_name<0, inside_unnamed_ns>())), "hidden");

BOOST_TEST_EQ( ((boost::pfr::get_name<0, function_local>())), "val");
}

void test_get_name_by_type() {
// FIXME: implement this
// using char_ref = std::reference_wrapper<char>;
Expand All @@ -52,6 +78,13 @@ void test_get_name_by_type() {
// BOOST_TEST_EQ( ((boost::pfr::get_name<char_ref, Aggregate>())), "c");
}

void test_get_name_by_type_without_linkage() {
// FIXME: implement this
// BOOST_TEST_EQ( ((boost::pfr::get_name<int, unnamed_t>())), "unnamed_first");
// BOOST_TEST_EQ( ((boost::pfr::get_name<float, unnamed_t>())), "unnamed_second");
// BOOST_TEST_EQ( ((boost::pfr::get_name<int, inside_unnamed_ns>())), "hidden");
}

void test_names_as_array() {
const auto expected = std::array<std::string_view, 4>{
"member1",
Expand All @@ -66,6 +99,18 @@ void test_names_as_array() {
}
}

void test_names_as_array_without_linkage() {
const auto expected = std::array<std::string_view, 2>{
"unnamed_first",
"unnamed_second"
};
const auto value = boost::pfr::names_as_array<unnamed_t>();
BOOST_TEST_EQ(expected.size(), value.size());
for (std::size_t i=0;i<expected.size();++i) {
BOOST_TEST_EQ(value[i], expected[i]);
}
}

void test_names_as_array_for_empty() {
const auto value = boost::pfr::names_as_array<empty>();
BOOST_TEST_EQ(value.size(), 0);
Expand All @@ -76,8 +121,11 @@ void test_names_as_array_for_empty() {

int main() {
testing::test_get_name_by_id();
testing::test_get_name_by_id_without_linkage();
testing::test_get_name_by_type();
testing::test_get_name_by_type_without_linkage();
testing::test_names_as_array();
testing::test_names_as_array_without_linkage();
testing::test_names_as_array_for_empty();

return boost::report_errors();
Expand Down

0 comments on commit 4c15f21

Please sign in to comment.