From e48f84dcb18acc04ae53e476e15d44a735a52123 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 18 Sep 2023 13:57:58 +0300 Subject: [PATCH] Harden the filed name checks and improve the diagnostics (#138) --- doc/Jamfile.v2 | 2 +- doc/pfr.qbk | 117 +++----------- example/get_name.cpp | 6 +- include/boost/pfr/config.hpp | 22 +-- include/boost/pfr/detail/core14_classic.hpp | 2 +- .../boost/pfr/detail/core_name20_static.hpp | 151 +++++++++--------- .../boost/pfr/detail/offset_based_getter.hpp | 2 +- include/boost/pfr/ops.hpp | 14 +- ...elds_names_wrongly_configured_compiler.cpp | 2 +- .../poor_fields_name.cpp} | 4 +- test/core_name/print_name.cpp | 19 ++- .../run/fields_names_internal_parser.cpp | 27 +--- 12 files changed, 141 insertions(+), 227 deletions(-) rename test/core_name/{run/fields_names_correctly_configured_compiler.cpp => compile-fail/poor_fields_name.cpp} (83%) diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index ce059b0..4f1ca36 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -32,7 +32,7 @@ local doxygen_params = "ALIASES= \\ \"forcedlink{1}=\\xmlonly\\endxmlonly boost::pfr::\\1\\xmlonly\\endxmlonly\" \\ \"podops=\\b See \\b Also : \\xmlonly\\endxmlonly 'Three ways of getting operators' \\xmlonly\\endxmlonly\" \\ - \"fnrefl=\\b See \\b Also : \\xmlonly\\endxmlonly 'Reflection of field names' \\xmlonly\\endxmlonly\" \\ + \"fnrefl=\\b See \\b Also : \\xmlonly\\endxmlonly 'Reflection of field names' \\xmlonly\\endxmlonly\" \\ \"customio=\\b See \\b Also : \\xmlonly\\endxmlonly 'Custom printing of aggregates' \\xmlonly\\endxmlonly for info on how to implement your own manipulator with custom format.\" \\ \"aggregate=\\xmlonly\\endxmlonly simple aggregate \\xmlonly\\endxmlonly\" \\ " diff --git a/doc/pfr.qbk b/doc/pfr.qbk index f9e8e72..d420efd 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -1,6 +1,6 @@ [library Boost.PFR [quickbook 1.6] - [version 2.1] + [version 2.2] [copyright 2016-2023 Antony Polukhin] [category Language Features Emulation] [license @@ -455,11 +455,11 @@ error: static_assert failed "====================> Boost.PFR: For safety reasons [endsect] -[section Reflection of field names ] +[section Reflection of field name ] [pfr_example_get_name] -See [link boost_pfr.limitations_of_field_names_refle [*Limitations of field names reflection]] and [link boost_pfr.limitations_and_configuration [*Limitations and Configuration]]. +See [link boost_pfr.limitations_and_configuration [*Limitations and Configuration]]. [endsect] @@ -490,27 +490,10 @@ 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` which variables are able to be declared in any other translation unit. -It's better not to use this feature with anonymous structure, local structure or a structure defined inside anonymous namespace. +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. -``` -struct external_simple_aggregate { - std::string name; - int age; - boost::uuids::uuid uuid; -}; -auto v1 = external_simple_aggregate{}; // can be declared outside via `extern` - -struct { - std::string name; - int age; - boost::uuids::uuid uuid; -} anonymous; // can't be declared outside -``` -Field's name extraction may work with a `SimpleAggregate` that does't satisfy such requirements, but the behavior tends to be non-portable. -Try using `-fpermissive` if you have any issue with it. - -As you see above extraction of field name feature has requirements of input type, additionally it has some requirements of compiler, see [link boost_pfr.limitations_of_field_names_refle [*Limitations of field names reflection]] for details. [h2 Configuration Macro] @@ -522,9 +505,9 @@ By default Boost.PFR [*auto-detects your compiler abilities] and automatically d [[*BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE*] [Define to `0` if you are hit by the template instantiation depth issues with `std::make_integer_sequence` and wish to use Boost.PFR version of that metafunction. Define to `1` to override Boost.PFR detection logic. ]] [[*BOOST_PFR_HAS_GUARANTEED_COPY_ELISION*] [Define to `0` if your compiler does not implement C++17 guaranteed copy elision properly and fails to reflect aggregates with non-movable fields. Define to `1` to override Boost.PFR detection logic. ]] [[*BOOST_PFR_ENABLE_IMPLICIT_REFLECTION*] [Define to `0` if you are hit by lots of non-effective choices made by implicitly reflection. Define to `1` to override Boost.PFR detection logic. ]] - [[*BOOST_PFR_CORE_NAME_ENABLED*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_CORE_NAME_ENABLED macro equal to 0. Defining this macro as 0 before including the header disables the ability to get a field's name. ]] - [[*BOOST_PFR_FUNCTION_SIGNATURE*] [On platforms which are unknown by Boost.PFR library, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_FUNCTION_SIGNATURE macro equal to "". Defining this macro before including the header might help the library to work on your specific platform. See details [link boost_pfr.limitations_of_field_names_refle [*here]]. ]] - [[*BOOST_PFR_CORE_NAME_PARSING*] [On platforms which are unknown by Boost.PFR library, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_CORE_NAME_PARSING macro equal to (0,0,false). Defining this macro before including the header might help the library to work on your specific platform. See details [link boost_pfr.limitations_of_field_names_refle [*here]]. ]] + [[*BOOST_PFR_CORE_NAME_ENABLED*] [On platforms where field name extraction is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_CORE_NAME_ENABLED macro equal to 0. Defining this macro as 0 before including the header disables the ability to get a field name. ]] + [[*BOOST_PFR_FUNCTION_SIGNATURE*] [For known compilers defined to a compiler specific macro, that outputs the whole function signature including non-type template parameters. ]] + [[*BOOST_PFR_CORE_NAME_PARSING*] [Describes extraction of field name from BOOST_PFR_FUNCTION_SIGNATURE macro. See details below. ]] [[*BOOST_PFR_ENABLED*] [On platforms where Boost.PFR is not supported, the `boost/pfr/config.hpp` header defines the BOOST_PFR_ENABLED macro equal to 0. Defining this macro as 0 before including the header disables the Boost.PFR library. ]] ] @@ -544,83 +527,27 @@ The Boost.PFRs reflection has some limitations that depend on a C++ Standard and The Boost.PFRs extraction of field name has some limitations that depend on a C++ Standard and compiler capabilities: -* T must be able to be extern. +* T should be usable like `extern T t;`, i.e. has a non-internal linkage. -[endsect] +[h2 Adjusting BOOST_PFR_CORE_NAME_PARSING] +`BOOST_PFR_CORE_NAME_PARSING` is already set up for most of the popular compilers. You need to adjust it only +if some static_assert in the library complained on `BOOST_PFR_CORE_NAME_PARSING`. -[section Limitations of field names reflection] +To do that: -Boost.PFRs extraction of field name has been tested and successfully work on many compilers. - -[section Define the BOOST_PFR_FUNCTION_SIGNATURE macro] - -If you get the following error during compilation -``` -error: static_assert failed "====================> Boost.PFR: Extraction of field name could not detect your compiler. - Please make the BOOST_PFR_FUNCTION_SIGNATURE macro use - correct compiler macro for getting the whole function name. - Define BOOST_PFR_CORE_NAME_PARSING to correct value after that." -``` -then you are using a compiler that was not tested with this library. - -BOOST_PFR_FUNCTION_SIGNATURE must be defined to a compiler specific macro, that outputs the *whole* -function signature including non-type template parameters. - - -[endsect] - -[section Fixing get_name() output] - -Let's assume the structure `namespace A { struct A { int fn; }; }` - -If the output of `boost::pfr::get_name<0, A::A>()` -returns not just `fn` but also a lot of text around the `fn` -or does not return name at all -then you are using a compiler that was not tested with this library and you need to setup the -BOOST_PFR_CORE_NAME_PARSING macro. - -Here is a short instruction: - -# get the output of `boost::pfr::get_name<0, A::A>()` -# define BOOST_PFR_CORE_NAME_PARSING to -`(skip_at_begin, skip_at_end, false, "")`, where - * `skip_at_begin` is equal to characters count before the first occurrence of `fn` in output - * `skip_at_end` is equal to characters count after last occurrence of `fn` in output -# check that `boost::pfr::get_name<0, A::A>()` returns "fn" -# if it does not return `fn`, then define BOOST_PFR_CORE_NAME_PARSING to -`(skip_at_begin, skip_at_end, true, "T = ")`, where +# Build `test/core_name/print_name.cpp` with your compiler and run it +# Define BOOST_PFR_CORE_NAME_PARSING to `(skip_at_begin, skip_at_end, "")`, where + * `skip_at_begin` is equal to characters count before the first occurrence of `user_defined_field` in output + * `skip_at_end` is equal to characters count after last occurrence of `user_defined_field` in output +# Check that `test/core_name/print_name.cpp` returns "user_defined_field" +# If it does not return `fn`, then define BOOST_PFR_CORE_NAME_PARSING to `(skip_at_begin, skip_at_end, "T = ")`, where * `skip_at_begin` is equal to `skip_at_begin` at step 2 * `skip_at_end` is equal to `skip_at_end` at step 2 - * `"T = "` is equal to characters that are right before the `fn` in output + * `"T = "` is equal to characters that are right before the `user_defined_field` in output, use `backward("T = ")` to search for the occurange in the string from the right # (optional, but highly recommended) [@https://github.com/boostorg/pfr/issues create ticket] with feature request to add your compiler to supported compilers list. Include -parameters provided to `BOOST_PFR_CORE_NAME_PARSING` macro. - - -Consider the following example: - -`boost::pfr::get_name<0, A::A>()` returns -"auto __cdecl boost::pfr::detail::name_of_field_implfn>(void) noexcept" while compiled with `-DBOOST_PFR_CORE_NAME_PARSING (0,0,false,"")`. Then you shall set -`skip_at_begin` to `sizeof("auto __cdecl boost::pfr::detail::name_of_field_impl<") - 1` -and `skip_at_end` to `sizeof(">(void) noexcept") - 1` and last parameter of macro to `"->"`. - -`` -#define BOOST_PFR_CORE_NAME_PARSING (52, 16, true, "->") -`` - -Another example: - -`boost::pfr::get_name<0, A::A>()` returns -"consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = A::A; auto ptr = (& a.A::A::fn)]" while compiled with `-DBOOST_PFR_CORE_NAME_PARSING (0,0,false,"")`. Then you shall set -`skip_at_begin` to `0` -and `skip_at_end` to `sizeof(")]") - 1` and last parameter of macro to `backward("::")`. - -`` -#define BOOST_PFR_CORE_NAME_PARSING (0, 2, true, backward("::")) -`` - -[endsect] +parameters provided to `BOOST_PFR_CORE_NAME_PARSING` macro [*and] the initial output if `test/core_name/print_name.cpp`. [endsect] diff --git a/example/get_name.cpp b/example/get_name.cpp index 5f5c7eb..b4c1b2e 100644 --- a/example/get_name.cpp +++ b/example/get_name.cpp @@ -13,7 +13,7 @@ #if BOOST_PFR_CORE_NAME_ENABLED && BOOST_PFR_USE_CPP17 //[pfr_example_get_name /*` - Since C++20 it's possible to read name of structure fields by index using Boost.PFR library. + Since C++20 it's possible to read name of a structure field by index using Boost.PFR library. The following example shows how to do it using [funcref boost::pfr::get_name]. Let's define some structure: @@ -28,8 +28,8 @@ struct foo { // defining structure /*` We can access field's names of that structure by index: */ -constexpr auto r1 = boost::pfr::get_name<0, foo>(); // reading name of field with index 0, returns string `some_integer` -constexpr auto r2 = boost::pfr::get_name<1, foo>(); // reading name of field with index 1, returns string `c` +constexpr std::string_view r1 = boost::pfr::get_name<0, foo>(); // returns "some_integer" +constexpr std::string_view r2 = boost::pfr::get_name<1, foo>(); // returns "c" //] [/pfr_example_get_name] #endif diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index faa6689..9c778c1 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -111,30 +111,16 @@ # endif #endif -#ifndef BOOST_PFR_FUNCTION_SIGNATURE -# if defined(__FUNCSIG__) -# define BOOST_PFR_FUNCTION_SIGNATURE __FUNCSIG__ -# elif defined(__PRETTY_FUNCTION__) \ - || defined(__GNUC__) \ - || defined(__clang__) -# define BOOST_PFR_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ -# else -# define BOOST_PFR_FUNCTION_SIGNATURE "" -# endif -#endif #ifndef BOOST_PFR_CORE_NAME_PARSING # if defined(_MSC_VER) -// sizeof("auto __cdecl boost::pfr::detail::name_of_field_impl<") - 1, sizeof(">(void) noexcept") - 1 -# define BOOST_PFR_CORE_NAME_PARSING (52 /*45 for non boost*/, 16, backward("->")) +# define BOOST_PFR_CORE_NAME_PARSING (sizeof("auto __cdecl boost::pfr::detail::name_of_field_impl<") - 1, sizeof(">(void) noexcept") - 1, backward("->")) # elif defined(__clang__) -// sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1 -# define BOOST_PFR_CORE_NAME_PARSING (64 /*57 for non boost*/, 2, backward(".")) +# define BOOST_PFR_CORE_NAME_PARSING (sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1, backward(".")) # elif defined(__GNUC__) -// sizeof("consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = ") - 1, sizeof(")]") - 1 -# define BOOST_PFR_CORE_NAME_PARSING (79 /*72 for non boost*/, 2, backward("::")) +# define BOOST_PFR_CORE_NAME_PARSING (sizeof("consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = ") - 1, sizeof(")]") - 1, backward("::")) # else -// Deafult parser for other platforms... Just skip nothing! +// Default parser for other platforms... Just skip nothing! # define BOOST_PFR_CORE_NAME_PARSING (0, 0, "") # endif #endif diff --git a/include/boost/pfr/detail/core14_classic.hpp b/include/boost/pfr/detail/core14_classic.hpp index d7e18ea..fd349d9 100644 --- a/include/boost/pfr/detail/core14_classic.hpp +++ b/include/boost/pfr/detail/core14_classic.hpp @@ -346,7 +346,7 @@ constexpr size_array get_type_offsets() noexcept { return offsets; } -///////////////////// Returns array of typeids and zeros if construtor of a type accepts sizeof...(I) parameters +///////////////////// Returns array of typeids and zeros if constructor of a type accepts sizeof...(I) parameters template constexpr void* flat_type_to_array_of_type_ids(std::size_t* types, std::index_sequence) noexcept { diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 086fe89..7334530 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -29,33 +29,24 @@ namespace boost { namespace pfr { namespace detail { struct core_name_skip { std::size_t size_at_begin; std::size_t size_at_end; - bool more_at_runtime; bool is_backward; std::string_view until_runtime; - consteval std::string_view fail() const noexcept { - return ""; - } - consteval std::string_view apply(std::string_view sv) const noexcept { + // We use std::min here to make the compiler diagnostic shorter and + // cleaner in case of misconfigured BOOST_PFR_CORE_NAME_PARSING sv.remove_prefix((std::min)(size_at_begin, sv.size())); sv.remove_suffix((std::min)(size_at_end, sv.size())); - if (!more_at_runtime) { - if (!until_runtime.empty()) - return fail(); ///< useless skip condition + if (until_runtime.empty()) { return sv; } - else { - // so, we're asked to skip more - if (until_runtime.empty()) - return fail(); ///< condition to skip more wasn't specified - const auto found = is_backward ? sv.rfind(until_runtime) - : sv.find(until_runtime); - ; - const auto cut_until = found + until_runtime.size(); - const auto safe_cut_until = (std::min)(cut_until, sv.size()); - return sv.substr(safe_cut_until); - } + + const auto found = is_backward ? sv.rfind(until_runtime) + : sv.find(until_runtime); + + const auto cut_until = found + until_runtime.size(); + const auto safe_cut_until = (std::min)(cut_until, sv.size()); + return sv.substr(safe_cut_until); } }; @@ -69,46 +60,16 @@ struct backward { consteval core_name_skip make_core_name_skip(std::size_t size_at_begin, std::size_t size_at_end, - bool more_at_runtime, std::string_view until_runtime) noexcept { - return core_name_skip{size_at_begin, size_at_end, more_at_runtime, false, until_runtime}; + return core_name_skip{size_at_begin, size_at_end, false, until_runtime}; } consteval core_name_skip make_core_name_skip(std::size_t size_at_begin, std::size_t size_at_end, - bool more_at_runtime, backward until_runtime) noexcept { - return core_name_skip{size_at_begin, size_at_end, more_at_runtime, true, until_runtime.value}; -} - -consteval core_name_skip make_core_name_skip(std::size_t size_at_begin, - std::size_t size_at_end, - auto until_runtime) noexcept -{ - return detail::make_core_name_skip(size_at_begin, size_at_end, true, until_runtime); -} - -template -consteval void assert_compile_time_legths() noexcept { - static_assert( - Condition, - "====================> Boost.PFR: Extraction of field name is misconfigured for your compiler. " - "Please define BOOST_PFR_CORE_NAME_PARSING to correct values. See section " - "Limitations of field's names reflection' of the documentation for more information." - ); -} - -template -consteval void failed_to_get_function_name() noexcept { - static_assert( - sizeof(T) && false, - "====================> Boost.PFR: Extraction of field name could not detect your compiler. " - "Please make the BOOST_PFR_FUNCTION_SIGNATURE macro use " - "correct compiler macro for getting the whole function name. " - "Define BOOST_PFR_CORE_NAME_PARSING to correct value after that." - ); + return core_name_skip{size_at_begin, size_at_end, true, until_runtime.value}; } // it might be compilation failed without this workaround sometimes @@ -121,31 +82,50 @@ consteval std::string_view clang_workaround(std::string_view value) noexcept template consteval auto name_of_field_impl() noexcept { + // Some of the following compiler specific macro may be defined only + // inside the function body: + +#ifndef BOOST_PFR_FUNCTION_SIGNATURE +# if defined(__FUNCSIG__) +# define BOOST_PFR_FUNCTION_SIGNATURE __FUNCSIG__ +# elif defined(__PRETTY_FUNCTION__) || defined(__GNUC__) || defined(__clang__) +# define BOOST_PFR_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ +# else +# define BOOST_PFR_FUNCTION_SIGNATURE "" +# endif +#endif + constexpr std::string_view sv = detail::clang_workaround(BOOST_PFR_FUNCTION_SIGNATURE); - if constexpr (sv.empty()) { - detail::failed_to_get_function_name(); - return detail::make_stdarray(0); - } else { - constexpr auto skip = detail::make_core_name_skip BOOST_PFR_CORE_NAME_PARSING; - static_assert( - skip.more_at_runtime || skip.until_runtime.empty(), - "====================> Boost.PFR: Parser configured in a wrong way. " - "It wasn't requested to skip more, but such skip condition was specified in vain. " - "Please read your definition of BOOST_PFR_CORE_NAME_PARSING macro patiently " - "and fix it." - ); - constexpr auto fn = skip.apply(sv); - auto res = std::array{}; - detail::assert_compile_time_legths(); + static_assert(!sv.empty(), + "====================> Boost.PFR: Field reflection parser configured in a wrong way. " + "Please define the BOOST_PFR_FUNCTION_SIGNATURE to a compiler specific macro, " + "that outputs the whole function signature including non-type template parameters." + ); - auto* out = res.data(); - for (auto x: fn) { - *out = x; - ++out; - } + constexpr auto skip = detail::make_core_name_skip BOOST_PFR_CORE_NAME_PARSING; + static_assert(skip.size_at_begin + skip.size_at_end + skip.until_runtime.size() < sv.size(), + "====================> Boost.PFR: Field reflection parser configured in a wrong way. " + "It attempts to skip more chars than available. " + "Please define BOOST_PFR_CORE_NAME_PARSING to correct values. See documentation section " + "'Limitations and Configuration' for more information." + ); + constexpr auto fn = skip.apply(sv); + static_assert( + !fn.empty(), + "====================> Boost.PFR: Extraction of field name is misconfigured for your compiler. " + "It skipped all the input, leaving the field name empty. " + "Please define BOOST_PFR_CORE_NAME_PARSING to correct values. See documentation section " + "'Limitations and Configuration' for more information." + ); + auto res = std::array{}; - return res; + auto* out = res.data(); + for (auto x: fn) { + *out = x; + ++out; } + + return res; } #ifdef __clang__ @@ -179,14 +159,35 @@ constexpr const T& make_clang_wrapper(const T& arg) noexcept { #endif +template +consteval auto name_of_field() noexcept { + // Sanity check: known field name must match the deduced one + static_assert( + sizeof(MsvcWorkaround) // do not trigger if `name_of_field()` is not used + && std::string_view{ + detail::name_of_field_impl< + core_name_skip, detail::make_clang_wrapper(std::addressof( + fake_object.size_at_begin + )) + >().data() + } == "size_at_begin", + "====================> Boost.PFR: Extraction of field name is misconfigured for your compiler. " + "It does not return the proper field name. " + "Please define BOOST_PFR_CORE_NAME_PARSING to correct values. See documentation section " + "'Limitations and Configuration' for more information." + ); + + return detail::name_of_field_impl(); +} + // Storing part of a string literal into an array minimizes the binary size. // -// Without passing 'T' into 'name_of_field_impl' different fields from different structures might have the same name! +// Without passing 'T' into 'name_of_field' different fields from different structures might have the same name! // See https://developercommunity.visualstudio.com/t/__FUNCSIG__-outputs-wrong-value-with-C/10458554 for details template -inline constexpr auto stored_name_of_field = detail::name_of_field_impl( - detail::tie_as_tuple(fake_object) + detail::tie_as_tuple(detail::fake_object) ))) >(); diff --git a/include/boost/pfr/detail/offset_based_getter.hpp b/include/boost/pfr/detail/offset_based_getter.hpp index b397be6..ee45f86 100644 --- a/include/boost/pfr/detail/offset_based_getter.hpp +++ b/include/boost/pfr/detail/offset_based_getter.hpp @@ -42,7 +42,7 @@ struct tuple_of_aligned_storage> { using type = sequence_tuple::tuple 4 ? 4 : alignof(Ts)) #else alignof(Ts) diff --git a/include/boost/pfr/ops.hpp b/include/boost/pfr/ops.hpp index ed6156c..f60b05a 100644 --- a/include/boost/pfr/ops.hpp +++ b/include/boost/pfr/ops.hpp @@ -78,7 +78,7 @@ namespace detail { } // namespace detail -/// \brief Compares lhs and rhs for equality using their own comparison and conversion operators; if no operators avalable returns \forcedlink{eq_fields}(lhs, rhs). +/// \brief Compares lhs and rhs for equality using their own comparison and conversion operators; if no operators available returns \forcedlink{eq_fields}(lhs, rhs). /// /// \returns true if lhs is equal to rhs; false otherwise template @@ -93,7 +93,7 @@ constexpr detail::enable_eq_comp_t eq(const T& lhs, const U& rhs) { } -/// \brief Compares lhs and rhs for inequality using their own comparison and conversion operators; if no operators avalable returns \forcedlink{ne_fields}(lhs, rhs). +/// \brief Compares lhs and rhs for inequality using their own comparison and conversion operators; if no operators available returns \forcedlink{ne_fields}(lhs, rhs). /// /// \returns true if lhs is not equal to rhs; false otherwise template @@ -108,7 +108,7 @@ constexpr detail::enable_ne_comp_t ne(const T& lhs, const U& rhs) { } -/// \brief Compares lhs and rhs for less-than using their own comparison and conversion operators; if no operators avalable returns \forcedlink{lt_fields}(lhs, rhs). +/// \brief Compares lhs and rhs for less-than using their own comparison and conversion operators; if no operators available returns \forcedlink{lt_fields}(lhs, rhs). /// /// \returns true if lhs is less than rhs; false otherwise template @@ -123,7 +123,7 @@ constexpr detail::enable_lt_comp_t lt(const T& lhs, const U& rhs) { } -/// \brief Compares lhs and rhs for greater-than using their own comparison and conversion operators; if no operators avalable returns \forcedlink{lt_fields}(lhs, rhs). +/// \brief Compares lhs and rhs for greater-than using their own comparison and conversion operators; if no operators available returns \forcedlink{lt_fields}(lhs, rhs). /// /// \returns true if lhs is greater than rhs; false otherwise template @@ -138,7 +138,7 @@ constexpr detail::enable_gt_comp_t gt(const T& lhs, const U& rhs) { } -/// \brief Compares lhs and rhs for less-equal using their own comparison and conversion operators; if no operators avalable returns \forcedlink{le_fields}(lhs, rhs). +/// \brief Compares lhs and rhs for less-equal using their own comparison and conversion operators; if no operators available returns \forcedlink{le_fields}(lhs, rhs). /// /// \returns true if lhs is less or equal to rhs; false otherwise template @@ -153,7 +153,7 @@ constexpr detail::enable_le_comp_t le(const T& lhs, const U& rhs) { } -/// \brief Compares lhs and rhs for greater-equal using their own comparison and conversion operators; if no operators avalable returns \forcedlink{ge_fields}(lhs, rhs). +/// \brief Compares lhs and rhs for greater-equal using their own comparison and conversion operators; if no operators available returns \forcedlink{ge_fields}(lhs, rhs). /// /// \returns true if lhs is greater or equal to rhs; false otherwise template @@ -168,7 +168,7 @@ constexpr detail::enable_ge_comp_t ge(const T& lhs, const U& rhs) { } -/// \brief Hashes value using its own std::hash specialization; if no std::hash specialization avalable returns \forcedlink{hash_fields}(value). +/// \brief Hashes value using its own std::hash specialization; if no std::hash specialization available returns \forcedlink{hash_fields}(value). /// /// \returns std::size_t with hash of the value template diff --git a/test/core_name/compile-fail/fields_names_wrongly_configured_compiler.cpp b/test/core_name/compile-fail/fields_names_wrongly_configured_compiler.cpp index 34fbea9..1ef6942 100644 --- a/test/core_name/compile-fail/fields_names_wrongly_configured_compiler.cpp +++ b/test/core_name/compile-fail/fields_names_wrongly_configured_compiler.cpp @@ -9,7 +9,7 @@ // #define BOOST_PFR_FUNCTION_SIGNATURE "dummy" -#define BOOST_PFR_CORE_NAME_PARSING (3,2,false,"") +#define BOOST_PFR_CORE_NAME_PARSING (3,2,"") #include struct A { int field; }; diff --git a/test/core_name/run/fields_names_correctly_configured_compiler.cpp b/test/core_name/compile-fail/poor_fields_name.cpp similarity index 83% rename from test/core_name/run/fields_names_correctly_configured_compiler.cpp rename to test/core_name/compile-fail/poor_fields_name.cpp index 3ba542c..4dc7539 100644 --- a/test/core_name/run/fields_names_correctly_configured_compiler.cpp +++ b/test/core_name/compile-fail/poor_fields_name.cpp @@ -9,7 +9,7 @@ // #define BOOST_PFR_FUNCTION_SIGNATURE " *[field] " -#define BOOST_PFR_CORE_NAME_PARSING (3,2,false,"") +#define BOOST_PFR_CORE_NAME_PARSING (3,2,"") #include #include @@ -17,7 +17,7 @@ struct A { int field; }; int main() { - BOOST_TEST_EQ( ((boost::pfr::get_name<0,A>())), "field"); + BOOST_TEST_EQ( (boost::pfr::get_name<0,A>()), "field"); return boost::report_errors(); } diff --git a/test/core_name/print_name.cpp b/test/core_name/print_name.cpp index d447cc1..a62cb92 100644 --- a/test/core_name/print_name.cpp +++ b/test/core_name/print_name.cpp @@ -13,19 +13,30 @@ // This cpp file: // * tests BOOST_PFR_CORE_NAME_PARSING macro // * outputs full name of the function so that PFRs extraction of field name could be adjust to new compiler without requesting regression tester's help -#define BOOST_PFR_CORE_NAME_PARSING (0,0,false,"") +#ifndef BOOST_PFR_CORE_NAME_PARSING +#define BOOST_PFR_CORE_NAME_PARSING (0,0,"") +#endif + #include namespace user_defined_namespace { struct user_defined_class { int user_defined_field; }; } +using namespace boost::pfr; + +// Cloned from core_name20_static.hpp but removed the sanity check +template +inline constexpr auto no_check_stored_name_of_field = detail::name_of_field_impl( + detail::tie_as_tuple(detail::fake_object) + ))) +>(); + int main() { - using namespace boost::pfr; - std::cout << "user_defined_namespace::user_defined_class::user_defined_field: " - << get_name<0, user_defined_namespace::user_defined_class>() << '\n'; + << no_check_stored_name_of_field.data() << '\n'; return 0; diff --git a/test/core_name/run/fields_names_internal_parser.cpp b/test/core_name/run/fields_names_internal_parser.cpp index b4d5802..c53fbd2 100644 --- a/test/core_name/run/fields_names_internal_parser.cpp +++ b/test/core_name/run/fields_names_internal_parser.cpp @@ -13,8 +13,7 @@ #include -namespace testing -{ +namespace testing { constexpr std::string_view fake_func_name = " ******************** [fake_text1->fake_text2->fake_text3] **********"; @@ -22,35 +21,25 @@ void test_general() { namespace detail = boost::pfr::detail; using detail::backward; - BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, false, "").apply(fake_func_name), "fake_text1->fake_text2->fake_text3"); + BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, "").apply(fake_func_name), "fake_text1->fake_text2->fake_text3"); + BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, backward("->")).apply(fake_func_name), "fake_text3"); + BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, "->").apply(fake_func_name), "fake_text2->fake_text3"); BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, backward("->")).apply(fake_func_name), "fake_text3"); BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, "->").apply(fake_func_name), "fake_text2->fake_text3"); - BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, true, backward("->")).apply(fake_func_name), "fake_text3"); - BOOST_TEST_EQ(detail::make_core_name_skip(23, 12, true, "->").apply(fake_func_name), "fake_text2->fake_text3"); -} - -void test_undefided_parser() -{ - namespace detail = boost::pfr::detail; - using detail::backward; - BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, backward("")).apply(fake_func_name), ""); - BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, "").apply(fake_func_name), ""); - BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, true, backward("")).apply(fake_func_name), ""); - BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, true, "").apply(fake_func_name), ""); } void test_identity_parser() { namespace detail = boost::pfr::detail; using detail::backward; - BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, false, backward("")).apply(fake_func_name), fake_func_name); - BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, false, "").apply(fake_func_name), fake_func_name); -} + BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, backward("")).apply(fake_func_name), fake_func_name); + BOOST_TEST_EQ(detail::make_core_name_skip(0, 0, "").apply(fake_func_name), fake_func_name); } +} // namespace testing + int main() { testing::test_general(); - testing::test_undefided_parser(); testing::test_identity_parser(); return boost::report_errors();