2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-01-19 04:22:13 +00:00

Harden the filed name checks and improve the diagnostics (#138)

This commit is contained in:
Antony Polukhin
2023-09-18 13:57:58 +03:00
parent ba25324b12
commit e48f84dcb1
12 changed files with 141 additions and 227 deletions

View File

@@ -32,7 +32,7 @@ local doxygen_params =
<doxygen:param>"ALIASES= \\
\"forcedlink{1}=\\xmlonly<link linkend='boost.pfr.\\1'>\\endxmlonly boost::pfr::\\1\\xmlonly</link>\\endxmlonly\" \\
\"podops=\\b See \\b Also : \\xmlonly<link linkend='boost_pfr.tutorial.three_ways_of_getting_operators'>\\endxmlonly 'Three ways of getting operators' \\xmlonly</link>\\endxmlonly\" \\
\"fnrefl=\\b See \\b Also : \\xmlonly<link linkend='boost_pfr.tutorial.reflection_of_field_names'>\\endxmlonly 'Reflection of field names' \\xmlonly</link>\\endxmlonly\" \\
\"fnrefl=\\b See \\b Also : \\xmlonly<link linkend='boost_pfr.tutorial.reflection_of_field_name'>\\endxmlonly 'Reflection of field names' \\xmlonly</link>\\endxmlonly\" \\
\"customio=\\b See \\b Also : \\xmlonly<link linkend='boost_pfr.tutorial.custom_printing_of_aggregates'>\\endxmlonly 'Custom printing of aggregates' \\xmlonly</link>\\endxmlonly for info on how to implement your own manipulator with custom format.\" \\
\"aggregate=\\xmlonly<link linkend='boost_pfr.limitations_and_configuration'>\\endxmlonly simple aggregate \\xmlonly</link>\\endxmlonly\" \\
"

View File

@@ -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_impl<struct A::A,&a->fn>(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]

View File

@@ -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

View File

@@ -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

View File

@@ -346,7 +346,7 @@ constexpr size_array<N> 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 <class T, std::size_t N, std::size_t... I>
constexpr void* flat_type_to_array_of_type_ids(std::size_t* types, std::index_sequence<I...>) noexcept
{

View File

@@ -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 <bool Condition>
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 <class T>
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 <class MsvcWorkaround, auto ptr>
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<MsvcWorkaround>(BOOST_PFR_FUNCTION_SIGNATURE);
if constexpr (sv.empty()) {
detail::failed_to_get_function_name<MsvcWorkaround>();
return detail::make_stdarray<char>(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<char, fn.size()+1>{};
detail::assert_compile_time_legths<!fn.empty()>();
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<char, fn.size()+1>{};
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 <class MsvcWorkaround, auto ptr>
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<core_name_skip>.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<MsvcWorkaround, ptr>();
}
// 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 <class T, std::size_t I>
inline constexpr auto stored_name_of_field = detail::name_of_field_impl<T,
inline constexpr auto stored_name_of_field = detail::name_of_field<T,
detail::make_clang_wrapper(std::addressof(detail::sequence_tuple::get<I>(
detail::tie_as_tuple(fake_object<T>)
detail::tie_as_tuple(detail::fake_object<T>)
)))
>();

View File

@@ -42,7 +42,7 @@ struct tuple_of_aligned_storage<sequence_tuple::tuple<Ts...>> {
using type = sequence_tuple::tuple<internal_aligned_storage<sizeof(Ts),
#if defined(__GNUC__) && __GNUC__ < 8 && !defined(__x86_64__) && !defined(__CYGWIN__)
// Before GCC-8 the `alignof` was returning the optimal alignment rather than the minimal one.
// We have to adjust the alignemnt because otherwise we get the wrong offset.
// We have to adjust the alignment because otherwise we get the wrong offset.
(alignof(Ts) > 4 ? 4 : alignof(Ts))
#else
alignof(Ts)

View File

@@ -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 <class T, class U>
@@ -93,7 +93,7 @@ constexpr detail::enable_eq_comp_t<T, U> 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 <class T, class U>
@@ -108,7 +108,7 @@ constexpr detail::enable_ne_comp_t<T, U> 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 <class T, class U>
@@ -123,7 +123,7 @@ constexpr detail::enable_lt_comp_t<T, U> 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 <class T, class U>
@@ -138,7 +138,7 @@ constexpr detail::enable_gt_comp_t<T, U> 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 <class T, class U>
@@ -153,7 +153,7 @@ constexpr detail::enable_le_comp_t<T, U> 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 <class T, class U>
@@ -168,7 +168,7 @@ constexpr detail::enable_ge_comp_t<T, U> 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 <class T>

View File

@@ -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 <boost/pfr/core_name.hpp>
struct A { int field; };

View File

@@ -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 <boost/pfr/core_name.hpp>
#include <boost/core/lightweight_test.hpp>
@@ -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();
}

View File

@@ -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 <boost/pfr/core_name.hpp>
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 <class T, std::size_t I>
inline constexpr auto no_check_stored_name_of_field = detail::name_of_field_impl<T,
detail::make_clang_wrapper(std::addressof(detail::sequence_tuple::get<I>(
detail::tie_as_tuple(detail::fake_object<T>)
)))
>();
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<user_defined_namespace::user_defined_class, 0>.data() << '\n';
return 0;

View File

@@ -13,8 +13,7 @@
#include <boost/core/lightweight_test.hpp>
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();