diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53f77f1a..c7b41ea6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: include: - - toolset: gcc-12 + - toolset: gcc-12 # Do not remove! It is the only toolset that tests misc/strip_boost_namespace.sh cxxstd: "03,11,14,17,2a" os: ubuntu-22.04 cxxflags: "cxxflags=--coverage -fsanitize=address,leak,undefined -fno-sanitize-recover=undefined" @@ -94,7 +94,7 @@ jobs: dist/bin/inspect libs/$LIBRARY - name: Test boost namespace stripping - if: ${{matrix.toolset == 'gcc-9'}} + if: ${{matrix.toolset == 'gcc-12'}} run: ../boost-root/libs/$LIBRARY/misc/strip_boost_namespace.sh - name: Prepare coverage data diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 1370aa24..ce059b08 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -32,6 +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\" \\ \"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 6a25c57d..f9e8e722 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -181,14 +181,16 @@ Boost.PFR adds the following out-of-the-box functionality for aggregate initiali * hash * IO streaming * access to members by index or type +* access to member's names by index * member type retrieval -* methods for cooperation with `std::tuple` +* methods for cooperation with `std::tuple` for members +* methods for cooperation with `std::array` for member's names * methods to visit each field of the structure * trait to detect potential ability to reflect type, and ability to override trait's decision in user-side code Boost.PFR is a header only library that does not depend on Boost. You can just copy the content of the "include" folder [@https://github.com/boostorg/pfr from the Boost.PFR github] into your project, and the library will work fine. For a version of the library without `boost::` namespace see [@https://github.com/apolukhin/pfr_non_boost PFR]. -[caution Recommended C++ Standards are C++17 and above. Library requires at least C++14! Pre C++14 compilers (C++11, C++03...) are not supported] +[caution Recommended C++ Standards are C++20 and above. C++17 completely enough for a user who doesn't want accessing name of structure member. Library requires at least C++14! Pre C++14 compilers (C++11, C++03...) are not supported] [endsect] @@ -203,6 +205,9 @@ Boost.PFR is a header only library that does not depend on Boost. You can just c [ [ [pfr_quick_examples_get] ] [ [funcref boost::pfr::get] ] +][ + [ [pfr_quick_examples_get_name] ] + [ [funcref boost::pfr::get_name] ] ][ [ [pfr_quick_examples_ops] ] [ @@ -269,6 +274,7 @@ Boost.PFR is a header only library that does not depend on Boost. You can just c [import ../example/sample_printing.cpp] [import ../example/get.cpp] +[import ../example/get_name.cpp] [section Why tuples are bad and aggregates are more preferable?] @@ -448,12 +454,21 @@ error: static_assert failed "====================> Boost.PFR: For safety reasons [endsect] + +[section Reflection of field names ] + +[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]]. + +[endsect] + [endsect] [section Limitations and Configuration] -[caution Recommended C++ Standards are C++17 and above. Library requires at least C++14! Pre C++14 compilers (C++11, C++03...) are not supported. ] +[caution Recommended C++ Standards are C++20 and above. C++17 completely enough for a user who doesn't want accessing name of structure member. Library requires at least C++14! Pre C++14 compilers (C++11, C++03...) are not supported. ] Boost.PFR library works with types that satisfy the requirements of `SimpleAggregate`: aggregate types without base classes, `const` fields, references, or C arrays: @@ -475,6 +490,28 @@ 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. + +``` +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] By default Boost.PFR [*auto-detects your compiler abilities] and automatically defines the configuration macro into appropriate values. If you wish to override that behavior, just define: @@ -485,6 +522,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_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. ]] ] @@ -502,6 +542,85 @@ The Boost.PFRs reflection has some limitations that depend on a C++ Standard and * T must be constexpr aggregate initializable and all its fields must be constexpr default constructible * [funcref boost::pfr::get], [funcref boost::pfr::structure_to_tuple], [funcref boost::pfr::structure_tie], [headerref boost/pfr/core.hpp boost::pfr::tuple_element] require T to be a POD type with built-in types only. +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. + +[endsect] + + +[section Limitations of field names reflection] + +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 + * `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 +# (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] [endsect] diff --git a/example/get_name.cpp b/example/get_name.cpp new file mode 100644 index 00000000..5f5c7eb3 --- /dev/null +++ b/example/get_name.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#include + +#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. + The following example shows how to do it using [funcref boost::pfr::get_name]. + + Let's define some structure: +*/ +#include + +struct foo { // defining structure + int some_integer; + char c; +}; + +/*` + 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` +//] [/pfr_example_get_name] +#endif + +int main() { +#if BOOST_PFR_CORE_NAME_ENABLED && BOOST_PFR_USE_CPP17 + if (r1 != "some_integer") return 1; + if (r2 != "c") return 2; +#endif + + return 0; +} diff --git a/example/quick_examples.cpp b/example/quick_examples.cpp index f1ca0f67..81666414 100644 --- a/example/quick_examples.cpp +++ b/example/quick_examples.cpp @@ -106,6 +106,23 @@ void test_examples() { //] } + // Disabled from testing since it's unportable +#if 0 + { +//[pfr_quick_examples_get_name + // Get name of field by index + + struct sample { + int f1; + long f2; + }; + + std::cout << boost::pfr::get_name<0, sample>() + << boost::pfr::get_name<1, sample>(); // Outputs: f1 f2 +//] + } +#endif + #if BOOST_PFR_USE_CPP17 || BOOST_PFR_USE_LOOPHOLE { //[pfr_quick_examples_structure_to_tuple diff --git a/include/boost/pfr.hpp b/include/boost/pfr.hpp index 5eb50852..8194ef4a 100644 --- a/include/boost/pfr.hpp +++ b/include/boost/pfr.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index c5ae2bd7..faa66894 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -98,6 +98,47 @@ # endif #endif +#ifndef BOOST_PFR_CORE_NAME_ENABLED +# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L)) +# if (defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911) \ + || (defined(__clang_major__) && __clang_major__ >= 12) +# define BOOST_PFR_CORE_NAME_ENABLED 1 +# else +# define BOOST_PFR_CORE_NAME_ENABLED 0 +# endif +# else +# define BOOST_PFR_CORE_NAME_ENABLED 0 +# 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("->")) +# 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(".")) +# 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("::")) +# else +// Deafult parser for other platforms... Just skip nothing! +# define BOOST_PFR_CORE_NAME_PARSING (0, 0, "") +# endif +#endif + #if defined(__has_cpp_attribute) # if __has_cpp_attribute(maybe_unused) # define BOOST_PFR_MAYBE_UNUSED [[maybe_unused]] diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp new file mode 100644 index 00000000..e64613d8 --- /dev/null +++ b/include/boost/pfr/core_name.hpp @@ -0,0 +1,88 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#ifndef BOOST_PFR_CORE_NAME_HPP +#define BOOST_PFR_CORE_NAME_HPP +#pragma once + +#include + +#include + +#include +#include +#include + +#include // for std::size_t + +#include + +/// \file boost/pfr/core_name.hpp +/// Contains functions \forcedlink{get_name} and \forcedlink{names_as_array} to know which names each field of any \aggregate has. +/// +/// \fnrefl for details. +/// +/// \b Synopsis: + +namespace boost { namespace pfr { + +/// \brief Returns name of a field with index `I` in \aggregate `T`. +/// +/// \b Example: +/// \code +/// struct my_struct { int i, short s; }; +/// +/// assert(boost::pfr::get_name<0, my_struct>() == "i"); +/// assert(boost::pfr::get_name<1, my_struct>() == "s"); +/// \endcode +template +constexpr +#ifdef BOOST_PFR_DOXYGEN_INVOKED +std::string_view +#else +auto +#endif +get_name() noexcept { + return detail::get_name(); +} + +// FIXME: implement this +// template +// constexpr auto get_name() noexcept { +// return detail::sequence_tuple::get_by_type_impl( detail::tie_as_names_tuple() ); +// } + +/// \brief Creates a `std::array` from names of fields of an \aggregate `T`. +/// +/// \b Example: +/// \code +/// struct my_struct { int i, short s; }; +/// std::array t = boost::pfr::names_as_array(); +/// assert(t[0] == "i"); +/// \endcode +template +constexpr +#ifdef BOOST_PFR_DOXYGEN_INVOKED +std::array> +#else +auto +#endif +names_as_array() noexcept { + return detail::make_stdarray_from_tietuple( + detail::tie_as_names_tuple(), + detail::make_index_sequence< tuple_size_v >(), + 1L + ); +} + +}} // namespace boost::pfr + +#endif // BOOST_PFR_CORE_NAME_HPP + diff --git a/include/boost/pfr/detail/core_name.hpp b/include/boost/pfr/detail/core_name.hpp new file mode 100644 index 00000000..f266ef20 --- /dev/null +++ b/include/boost/pfr/detail/core_name.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#ifndef BOOST_PFR_DETAIL_CORE_NAME_HPP +#define BOOST_PFR_DETAIL_CORE_NAME_HPP +#pragma once + +#include + +// Each core_name provides `boost::pfr::detail::get_name` and +// `boost::pfr::detail::tie_as_names_tuple` functions. +// +// The whole functional of extracting field's names is build on top of those +// two functions. +#if BOOST_PFR_CORE_NAME_ENABLED +#include +#else +#include +#endif + +#endif // BOOST_PFR_DETAIL_CORE_NAME_HPP + diff --git a/include/boost/pfr/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp new file mode 100644 index 00000000..115e8382 --- /dev/null +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -0,0 +1,43 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#ifndef BOOST_PFR_DETAIL_CORE_NAME14_DISABLED_HPP +#define BOOST_PFR_DETAIL_CORE_NAME14_DISABLED_HPP +#pragma once + +#include +#include + +namespace boost { namespace pfr { namespace detail { + +template +constexpr auto get_name() noexcept { + static_assert( + sizeof(T) && false, + "====================> Boost.PFR: Field's names extracting functionality requires C++20." + ); + + return nullptr; +} + +template +constexpr auto tie_as_names_tuple() noexcept { + static_assert( + sizeof(T) && false, + "====================> Boost.PFR: Field's names extracting functionality requires C++20." + ); + + return detail::sequence_tuple::make_sequence_tuple(); +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_CORE_NAME14_DISABLED_HPP + diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp new file mode 100644 index 00000000..08351104 --- /dev/null +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -0,0 +1,229 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#ifndef BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP +#define BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for std::addressof + +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 { + 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 + 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); + } + } +}; + +struct backward { + explicit consteval backward(std::string_view value) noexcept + : value(value) + {} + + std::string_view value; +}; + +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}; +} + +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." + ); +} + +// it might be compilation failed without this workaround sometimes +// See https://github.com/llvm/llvm-project/issues/41751 for details +template +consteval std::string_view clang_workaround(std::string_view value) noexcept +{ + return value; +} + +template +consteval auto name_of_field_impl() noexcept { + 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(); + + auto* out = res.data(); + for (auto x: fn) { + *out = x; + ++out; + } + + return res; + } +} + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundefined-var-template" + +// clang 16 and earlier don't support address of non-static member as template parameter +// but fortunately it's possible to use C++20 non-type template parameters in another way +// even in clang 16 and more older clangs +// all we need is to wrap pointer into 'clang_wrapper_t' and then pass it into template +template +struct clang_wrapper_t { + T v; +}; +template +clang_wrapper_t(T) -> clang_wrapper_t; + +template +constexpr auto make_clang_wrapper(const T& arg) noexcept { + return clang_wrapper_t{arg}; +} + +#else + +template +constexpr const T& make_clang_wrapper(const T& arg) noexcept { + // It's everything OK with address of non-static member as template parameter support on this compiler + // so we don't need a wrapper here, just pass the pointer into template + return arg; +} + +#endif + +// Without passing 'T' into 'name_of_field_impl' 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 +constexpr auto stored_name_of_field = detail::name_of_field_impl( + detail::tie_as_tuple(fake_object) +)))>(); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +template +constexpr auto tie_as_names_tuple_impl(std::index_sequence) noexcept { + return detail::sequence_tuple::make_sequence_tuple(std::string_view{stored_name_of_field.data()}...); +} + +template +constexpr std::string_view get_name() noexcept { + static_assert( + !std::is_union::value, + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." + ); + static_assert( + sizeof(T) && BOOST_PFR_USE_CPP17, + "====================> Boost.PFR: Extraction of field's names is allowed only when the BOOST_PFR_USE_CPP17 macro enabled." + ); + + return stored_name_of_field.data(); +} + +template +constexpr auto tie_as_names_tuple() noexcept { + static_assert( + !std::is_union::value, + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." + ); + static_assert( + sizeof(T) && BOOST_PFR_USE_CPP17, + "====================> Boost.PFR: Extraction of field's names is allowed only when the BOOST_PFR_USE_CPP17 macro enabled." + ); + + return detail::tie_as_names_tuple_impl(detail::make_index_sequence()>{}); +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP + diff --git a/include/boost/pfr/detail/fake_object.hpp b/include/boost/pfr/detail/fake_object.hpp new file mode 100644 index 00000000..2e37e978 --- /dev/null +++ b/include/boost/pfr/detail/fake_object.hpp @@ -0,0 +1,25 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#ifndef BOOST_PFR_DETAIL_FAKE_OBJECT_HPP +#define BOOST_PFR_DETAIL_FAKE_OBJECT_HPP +#pragma once + +#include + +namespace boost { namespace pfr { namespace detail { + +template +extern const T fake_object; + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_FAKE_OBJECT_HPP + diff --git a/include/boost/pfr/detail/sequence_tuple.hpp b/include/boost/pfr/detail/sequence_tuple.hpp index f8f28eaa..fcbb8370 100644 --- a/include/boost/pfr/detail/sequence_tuple.hpp +++ b/include/boost/pfr/detail/sequence_tuple.hpp @@ -127,6 +127,9 @@ struct tuple: tuple_base< detail::index_sequence_for, Values... >::tuple_base; + + constexpr static std::size_t size() noexcept { return sizeof...(Values); } + constexpr static bool empty() noexcept { return size() == 0; } }; @@ -165,6 +168,10 @@ using tuple_element = std::remove_reference< decltype( ::boost::pfr::detail::sequence_tuple::get( std::declval() ) ) >; +template +constexpr auto make_sequence_tuple(Args... args) noexcept { + return ::boost::pfr::detail::sequence_tuple::tuple{ args... }; +} }}}} // namespace boost::pfr::detail::sequence_tuple diff --git a/include/boost/pfr/detail/stdarray.hpp b/include/boost/pfr/detail/stdarray.hpp new file mode 100644 index 00000000..afaddd2d --- /dev/null +++ b/include/boost/pfr/detail/stdarray.hpp @@ -0,0 +1,41 @@ +// Copyright (c) 2023 Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PFR_DETAIL_STDARRAY_HPP +#define BOOST_PFR_DETAIL_STDARRAY_HPP +#pragma once + +#include + +#include // metaprogramming stuff +#include +#include // for std::common_type_t +#include + +#include + +namespace boost { namespace pfr { namespace detail { + +template +constexpr auto make_stdarray(const Types&... t) noexcept { + return std::array, sizeof...(Types)>{t...}; +} + +template +constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence, int) noexcept { + return detail::make_stdarray( + boost::pfr::detail::sequence_tuple::get(t)... + ); +} + +template +constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence<>, long) noexcept { + return std::array{}; +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_STDARRAY_HPP + diff --git a/misc/generate_fields_names_big.cpp.py b/misc/generate_fields_names_big.cpp.py new file mode 100644 index 00000000..deb824b2 --- /dev/null +++ b/misc/generate_fields_names_big.cpp.py @@ -0,0 +1,267 @@ +# Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +# Initial implementation by Bela Schaum, https://github.com/schaumb +# The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +# + +############################################################################################################################ + +import sys +import string +import random + +# a bigger value might lead to compiler out of heap space error on MSVC +STRUCT_COUNT = 50 +MAX_FIELD_COUNT = 50 + +MAIN_TEMPLATE = """// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov, Antony Polukhin. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Generated by misc/generate_fields_names_big.cpp.py + +#include +#include + +#include + +namespace testing { + +template +auto make_stdarray(const Types&... t) { + return std::array, sizeof...(Types)>{t...}; +} + +%STRUCTS_LIST% + +%TEST_GET_NAME_DEFINITIONS_LIST% + +%TEST_GET_NAMES_AS_ARRAY_DEFINITIONS_LIST% + + +} // namespace testing + +int main() { + %TEST_GET_NAME_CALLS_LIST% + %TEST_GET_NAMES_AS_ARRAY_CALLS_LIST% + + return boost::report_errors(); +} +""" + +STRUCT_TEMPLATE = """struct Aggregate%STRUCT_ID% { + %FIELD_DEFINITIONS_LIST% +}; +""" + +STRUCT_FIELD_DEFINITION_TEMPLATE = """int %FIELD_NAME%; +""" + +TEST_GET_NAME_TEMPLATE = """void test_get_name_%TEST_ID%() { + %CHECKERS_LIST% +} +""" + +TEST_GET_NAME_CHECKER_TEMPLATE = """BOOST_TEST_EQ( ((boost::pfr::get_name<%FIELD_ID%, Aggregate%STRUCT_ID%>())), "%FIELD_NAME%"); +""" + +TEST_GET_NAMES_AS_ARRAY_TEMPLATE = """void test_names_as_array_%TEST_ID%() { + const auto expected = make_stdarray( + %FIELD_NAMES_LIST% + ); + const auto value = boost::pfr::names_as_array(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i /dev/null; then +if g++-12 -std=c++2a -DPFR_ENABLE_GET_NAME_STATIC=1 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /dev/null; then echo -n "OK" else echo -n "FAIL" exit 2 fi -if g++-9 -std=c++17 -DPFR_USE_LOOPHOLE=1 -DPFR_USE_CPP17=0 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /dev/null; then - echo -n ", OK" +if g++-12 -std=c++2a -DPFR_ENABLE_GET_NAME_STATIC=0 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /dev/null; then + echo -n "OK" else - echo -n ", FAIL" + echo -n "FAIL" exit 3 fi -if g++-9 -std=c++17 -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=0 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/get.cpp && ./a.out > /dev/null; then +if g++-12 -std=c++2a -DPFR_ENABLE_GET_NAME_STATIC=1 -DBOOST_PFR_USE_CPP17=1 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/get_name.cpp && ./a.out > /dev/null; then echo -e ", OK" else echo -e ", FAIL" exit 4 fi +if g++-12 -std=c++2a -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=1 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /dev/null; then + echo -n "OK" +else + echo -n "FAIL" + exit 5 +fi +if g++-12 -std=c++2a -DPFR_USE_LOOPHOLE=1 -DPFR_USE_CPP17=0 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /dev/null; then + echo -n ", OK" +else + echo -n ", FAIL" + exit 6 +fi + +if g++-12 -std=c++2a -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=0 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/get.cpp && ./a.out > /dev/null; then + echo -e ", OK" +else + echo -e ", FAIL" + exit 7 +fi + rm a.out || : echo "***** Done" diff --git a/test/Jamfile b/test/Jamfile index de6f4c08..2d7c089c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -6,4 +6,5 @@ # build-project config ; -build-project core ; \ No newline at end of file +build-project core ; +build-project core_name ; diff --git a/test/config/print_config.cpp b/test/config/print_config.cpp index e6c2905e..b9c0e77e 100644 --- a/test/config/print_config.cpp +++ b/test/config/print_config.cpp @@ -4,6 +4,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include // inclusion of an another PFR header may fail when BOOST_PFR_ENABLED=0 +#include #include @@ -14,6 +15,9 @@ int main() { << "BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE == " << BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE << '\n' << "BOOST_PFR_HAS_GUARANTEED_COPY_ELISION == " << BOOST_PFR_HAS_GUARANTEED_COPY_ELISION << '\n' << "BOOST_PFR_ENABLE_IMPLICIT_REFLECTION == " << BOOST_PFR_ENABLE_IMPLICIT_REFLECTION << '\n' + << "BOOST_PFR_CORE_NAME_ENABLED == " << BOOST_PFR_CORE_NAME_ENABLED << '\n' + << "BOOST_PFR_FUNCTION_SIGNATURE == " << BOOST_PP_STRINGIZE(BOOST_PFR_FUNCTION_SIGNATURE) << '\n' + << "BOOST_PFR_CORE_NAME_PARSING == " << BOOST_PP_STRINGIZE(BOOST_PFR_CORE_NAME_PARSING) << '\n' << "BOOST_PFR_ENABLED == " << BOOST_PFR_ENABLED << '\n' << "__cplusplus == " << __cplusplus << '\n' #ifdef __cpp_structured_bindings diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 new file mode 100644 index 00000000..13b1e4cc --- /dev/null +++ b/test/core_name/Jamfile.v2 @@ -0,0 +1,60 @@ +# Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +# Initial implementation by Bela Schaum, https://github.com/schaumb +# The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +# + +import testing ; +import ../../config/checks/config : requires ; + +########## BEGIN of helpers to detect C++20 features support + +actions mp_simple_run_action +{ + $(>) > $(<) +} + +rule mp-run-simple ( sources + : args * : input-files * : requirements * : target-name ) +{ + exe $(target-name)_exe : $(sources) : $(requirements) ; + explicit $(target-name)_exe ; + make $(target-name).output : $(target-name)_exe : @mp_simple_run_action ; + explicit $(target-name).output ; + alias $(target-name) : $(target-name).output ; +} + +mp-run-simple cxx20_address_of_non_static_member_tplarg_detection.cpp : : : : compiler_supports_cxx20_address_of_non_static_member_tplarg ; +explicit compiler_supports_cxx20_address_of_non_static_member_tplarg ; + +mp-run-simple cxx20_nontype_tplarg_detection.cpp : : : : compiler_supports_cxx20_nontype_tplarg ; +explicit compiler_supports_cxx20_nontype_tplarg ; + +########## END of helpers to detect C++20 features support + +project + : source-location . + : requirements + BOOST_PFR_DETAIL_STRICT_RVALUE_TESTING=1 + [ check-target-builds ../core_name//compiler_supports_cxx20_address_of_non_static_member_tplarg : : [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_tplarg : : no ] ] + ; + +test-suite pfr_name_tests + : + [ run print_name.cpp : : : always_show_run_output ] + ; + +for local source_file in [ glob ./run/*.cpp ] +{ + pfr_name_tests += [ run $(source_file) : : : msvc:"/utf-8" msvc:"/bigobj" : ] ; +} + +for local source_file in [ glob ./compile-fail/*.cpp ] +{ + pfr_name_tests += [ compile-fail $(source_file) : : ] ; +} + + diff --git a/test/core_name/compile-fail/fields_names_could_not_detect_compiler.cpp b/test/core_name/compile-fail/fields_names_could_not_detect_compiler.cpp new file mode 100644 index 00000000..894feb91 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_could_not_detect_compiler.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#define BOOST_PFR_FUNCTION_SIGNATURE "" +#include + +struct A { int field; }; + +int main() { + (void)boost::pfr::get_name<0, A>(); // Must be a compile time error +} + + diff --git a/test/core_name/compile-fail/fields_names_disabled_via_macro_00.cpp b/test/core_name/compile-fail/fields_names_disabled_via_macro_00.cpp new file mode 100644 index 00000000..7cda619b --- /dev/null +++ b/test/core_name/compile-fail/fields_names_disabled_via_macro_00.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#define BOOST_PFR_CORE_NAME_ENABLED 0 +#include + +struct A { int field; }; + +int main() { + (void)boost::pfr::get_name<0, A>(); // Must be a compile time error +} + + diff --git a/test/core_name/compile-fail/fields_names_disabled_via_macro_01.cpp b/test/core_name/compile-fail/fields_names_disabled_via_macro_01.cpp new file mode 100644 index 00000000..392e3979 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_disabled_via_macro_01.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#define BOOST_PFR_CORE_NAME_ENABLED 0 +#include + +struct A { int field; }; + +int main() { + (void)boost::pfr::names_as_array(); // Must be a compile time error +} + + diff --git a/test/core_name/compile-fail/fields_names_get_name_on_union.cpp b/test/core_name/compile-fail/fields_names_get_name_on_union.cpp new file mode 100644 index 00000000..e4f9e633 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_get_name_on_union.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#include + +union test_union { + const char* c; + int i; +}; + +int main() { + (void)boost::pfr::get_name<0, test_union>(); // Must be a compile time error +} + + diff --git a/test/core_name/compile-fail/fields_names_misconfigured_compiler.cpp b/test/core_name/compile-fail/fields_names_misconfigured_compiler.cpp new file mode 100644 index 00000000..bdbb5ca5 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_misconfigured_compiler.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#define BOOST_PFR_FUNCTION_SIGNATURE "dummy" +#define BOOST_PFR_CORE_NAME_PARSING (0,0,"") +#include + +struct A { int field; }; + +int main() { + (void)boost::pfr::get_name<0, A>(); // Must be a compile time error +} + + diff --git a/test/core_name/compile-fail/fields_names_names_as_array_on_union.cpp b/test/core_name/compile-fail/fields_names_names_as_array_on_union.cpp new file mode 100644 index 00000000..d61dd0d7 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_names_as_array_on_union.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#include + +union test_union { + const char* c; + int i; +}; + +int main() { + (void)boost::pfr::names_as_array(); // Must be a compile time error +} + + 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 new file mode 100644 index 00000000..34fbea91 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_wrongly_configured_compiler.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#define BOOST_PFR_FUNCTION_SIGNATURE "dummy" +#define BOOST_PFR_CORE_NAME_PARSING (3,2,false,"") +#include + +struct A { int field; }; + +int main() { + (void)boost::pfr::get_name<0, A>(); // Must be a compile time error +} + + diff --git a/test/core_name/cxx20_address_of_non_static_member_tplarg_detection.cpp b/test/core_name/cxx20_address_of_non_static_member_tplarg_detection.cpp new file mode 100644 index 00000000..e045575a --- /dev/null +++ b/test/core_name/cxx20_address_of_non_static_member_tplarg_detection.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +template +class X {}; + +struct S +{ + int m; +} s; + +X<&s.m> x4; + +int main() {} + + diff --git a/test/core_name/cxx20_nontype_tplarg_detection.cpp b/test/core_name/cxx20_nontype_tplarg_detection.cpp new file mode 100644 index 00000000..277bd6c4 --- /dev/null +++ b/test/core_name/cxx20_nontype_tplarg_detection.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +template +class X {}; + +template +struct Store +{ + T v; +}; + +template +Store(T) -> Store; + +struct S +{ + int m; +} s; + +X x4; + +int main() {} + + diff --git a/test/core_name/print_name.cpp b/test/core_name/print_name.cpp new file mode 100644 index 00000000..d447cc13 --- /dev/null +++ b/test/core_name/print_name.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#include + +// 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,"") +#include + +namespace user_defined_namespace { + struct user_defined_class { int user_defined_field; }; +} + +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'; + + + return 0; +} + diff --git a/test/core_name/run/fields_names.cpp b/test/core_name/run/fields_names.cpp new file mode 100644 index 00000000..ca7acbdf --- /dev/null +++ b/test/core_name/run/fields_names.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#include + +#include +#include // for std::reference_wrapper +#include + +namespace testing { + +struct nonconstexpr { + nonconstexpr() {}; +}; + +struct Aggregate { + int member1; + nonconstexpr this_is_a_name; + std::reference_wrapper c; + std::string Forth; +}; + +struct A { + int first; + int second; +}; + +struct empty {}; + +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"); + BOOST_TEST_EQ( ((boost::pfr::get_name<2, Aggregate>())), "c"); + BOOST_TEST_EQ( ((boost::pfr::get_name<3, Aggregate>())), "Forth"); + + BOOST_TEST_EQ( ((boost::pfr::get_name<0, A>())), "first"); + BOOST_TEST_EQ( ((boost::pfr::get_name<1, A>())), "second"); +} + +void test_get_name_by_type() { +// FIXME: implement this +// using char_ref = std::reference_wrapper; +// BOOST_TEST_EQ( ((boost::pfr::get_name())), "member1"); +// BOOST_TEST_EQ( ((boost::pfr::get_name())), "this_is_a_name"); +// BOOST_TEST_EQ( ((boost::pfr::get_name())), "c"); +} + +void test_names_as_array() { + const auto expected = std::array{ + "member1", + "this_is_a_name", + "c", + "Forth" + }; + const auto value = boost::pfr::names_as_array(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i(); + BOOST_TEST_EQ(value.size(), 0); + BOOST_TEST_EQ(value.empty(), true); +} + +} // namespace testing + +int main() { + testing::test_get_name_by_id(); + testing::test_get_name_by_type(); + testing::test_names_as_array(); + testing::test_names_as_array_for_empty(); + + return boost::report_errors(); +} + + diff --git a/test/core_name/run/fields_names_big.cpp b/test/core_name/run/fields_names_big.cpp new file mode 100644 index 00000000..da1f2f4a --- /dev/null +++ b/test/core_name/run/fields_names_big.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov, Antony Polukhin. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Generated by misc/generate_fields_names_big.cpp.py + +#include +#include + +#include + +namespace testing { + +template +auto make_stdarray(const Types&... t) { + return std::array, sizeof...(Types)>{t...}; +} + +struct Aggregate1 { + int EXuT_0EBItazIzOuovbOSyOYGqbJw1uQ; + +}; +struct Aggregate2 { + int SnihXdx4VbVTcwKm2PGOy8gBYi; +int l_hKC0UCDgf9akyA6pr1IG; + +}; +struct Aggregate3 { + int GWYXwjwOCC5uOWkmZjrjk95yaOQbVfUbSJRCoQoRyPXjzp7x2c7WMwvck0JeOg; +int xdtwasd3bFi2mErdk64LyyWKcOb5gudT50eCBMUHraYYmd8Yxp9M0pO1DmDHWQZ4LEshxseqUjzoTeuwnVwA6uvStNpN0ZtZ; +int shqTJk7vA32s2DQB5o7TyecNLMHLUogzWYO_NwRsgX; + +}; +struct Aggregate4 { + int ofYcwHz8V6YMDPsHqyIB5TVZckFc3cK0Da; +int HiGE0sOlKCO; +int F5Nrv0LUdUSmrzLgPmtENIEe5uJQyRXC0owDVh9IjRBdA_aSEqiMx_EhWXEkbddK5MCgCv223s9EXlMc55ByxpG6XYbXS6nHywEy; +int emjjbXYK; + +}; +struct Aggregate5 { + int M0u7SIZSVWJ0KqQygT_6npmZv1XzZI5dJcwjQuqq6lIdlSJSpnAKhOg82qyVywwNq3cvvmnmAv7; +int SXEUFB6z; +int tDxT; +int sV2m6xg3MxKN1Xln2dXyBh8rkF7lUUfHIK8nK4FRzru2DXeT; +int ycEXe3x03PvbXqFJzOKMq8i4XLAZfyY2i4HONhv1Wx_; + +}; + + +void test_get_name_1() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate1>())), "EXuT_0EBItazIzOuovbOSyOYGqbJw1uQ"); + +} +void test_get_name_2() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate2>())), "SnihXdx4VbVTcwKm2PGOy8gBYi"); +BOOST_TEST_EQ( ((boost::pfr::get_name<1, Aggregate2>())), "l_hKC0UCDgf9akyA6pr1IG"); + +} +void test_get_name_3() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate3>())), "GWYXwjwOCC5uOWkmZjrjk95yaOQbVfUbSJRCoQoRyPXjzp7x2c7WMwvck0JeOg"); +BOOST_TEST_EQ( ((boost::pfr::get_name<1, Aggregate3>())), "xdtwasd3bFi2mErdk64LyyWKcOb5gudT50eCBMUHraYYmd8Yxp9M0pO1DmDHWQZ4LEshxseqUjzoTeuwnVwA6uvStNpN0ZtZ"); +BOOST_TEST_EQ( ((boost::pfr::get_name<2, Aggregate3>())), "shqTJk7vA32s2DQB5o7TyecNLMHLUogzWYO_NwRsgX"); + +} +void test_get_name_4() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate4>())), "ofYcwHz8V6YMDPsHqyIB5TVZckFc3cK0Da"); +BOOST_TEST_EQ( ((boost::pfr::get_name<1, Aggregate4>())), "HiGE0sOlKCO"); +BOOST_TEST_EQ( ((boost::pfr::get_name<2, Aggregate4>())), "F5Nrv0LUdUSmrzLgPmtENIEe5uJQyRXC0owDVh9IjRBdA_aSEqiMx_EhWXEkbddK5MCgCv223s9EXlMc55ByxpG6XYbXS6nHywEy"); +BOOST_TEST_EQ( ((boost::pfr::get_name<3, Aggregate4>())), "emjjbXYK"); + +} +void test_get_name_5() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate5>())), "M0u7SIZSVWJ0KqQygT_6npmZv1XzZI5dJcwjQuqq6lIdlSJSpnAKhOg82qyVywwNq3cvvmnmAv7"); +BOOST_TEST_EQ( ((boost::pfr::get_name<1, Aggregate5>())), "SXEUFB6z"); +BOOST_TEST_EQ( ((boost::pfr::get_name<2, Aggregate5>())), "tDxT"); +BOOST_TEST_EQ( ((boost::pfr::get_name<3, Aggregate5>())), "sV2m6xg3MxKN1Xln2dXyBh8rkF7lUUfHIK8nK4FRzru2DXeT"); +BOOST_TEST_EQ( ((boost::pfr::get_name<4, Aggregate5>())), "ycEXe3x03PvbXqFJzOKMq8i4XLAZfyY2i4HONhv1Wx_"); + +} + + +void test_names_as_array_1() { + const auto expected = make_stdarray( + std::string_view{"EXuT_0EBItazIzOuovbOSyOYGqbJw1uQ"} + + ); + const auto value = boost::pfr::names_as_array(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i + +#include +#include + +struct nonconstexpr { + nonconstexpr() {}; +}; + +struct Aggregate { + int member1; + nonconstexpr this_is_a_name; + std::reference_wrapper c; + std::string Forth; +}; + +static_assert(boost::pfr::get_name<0, Aggregate>() == "member1"); +static_assert(boost::pfr::get_name<1, Aggregate>() == "this_is_a_name"); +static_assert(boost::pfr::get_name<2, Aggregate>() == "c"); +static_assert(boost::pfr::get_name<3, Aggregate>() == "Forth"); + +constexpr auto names_array = boost::pfr::names_as_array(); +static_assert(names_array.size() == 4); +static_assert(names_array[0] == "member1"); +static_assert(names_array[1] == "this_is_a_name"); +static_assert(names_array[2] == "c"); +static_assert(names_array[3] == "Forth"); + +int main() {} + diff --git a/test/core_name/run/fields_names_correctly_configured_compiler.cpp b/test/core_name/run/fields_names_correctly_configured_compiler.cpp new file mode 100644 index 00000000..3ba542ca --- /dev/null +++ b/test/core_name/run/fields_names_correctly_configured_compiler.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#define BOOST_PFR_FUNCTION_SIGNATURE " *[field] " +#define BOOST_PFR_CORE_NAME_PARSING (3,2,false,"") +#include + +#include + +struct A { int field; }; + +int main() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0,A>())), "field"); + + return boost::report_errors(); +} + + diff --git a/test/core_name/run/fields_names_internal_parser.cpp b/test/core_name/run/fields_names_internal_parser.cpp new file mode 100644 index 00000000..b4d5802b --- /dev/null +++ b/test/core_name/run/fields_names_internal_parser.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#include +#include + +#include + +namespace testing +{ + +constexpr std::string_view fake_func_name = " ******************** [fake_text1->fake_text2->fake_text3] **********"; + +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, 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); +} +} + +int main() { + testing::test_general(); + testing::test_undefided_parser(); + testing::test_identity_parser(); + + return boost::report_errors(); +} + diff --git a/test/core_name/run/fields_names_nonascii.cpp b/test/core_name/run/fields_names_nonascii.cpp new file mode 100644 index 00000000..56c25fc7 --- /dev/null +++ b/test/core_name/run/fields_names_nonascii.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2023 Bela Schaum, X-Ryl669, Denis Mikhailov, Antony Polukhin. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +/* Tesing non-ASCII field names, so we have to add th following suppression: +boost-no-inspect +*/ + +#include + +#include + +namespace testing { + +struct Aggregate { + int _πривет_мир; +}; + +void test_get_name() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate>())), "_πривет_мир"); +} + +void test_names_as_array() { + const auto expected = std::array{ + "_πривет_мир" + }; + const auto value = boost::pfr::names_as_array(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i