From fc89551a19a28a2644fe7c0299b9cb79ed4bbc5a Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 25 Jun 2023 22:40:31 +0000 Subject: [PATCH 01/30] Add methods to extract fields names --- doc/pfr.qbk | 3 + include/boost/pfr.hpp | 1 + include/boost/pfr/config.hpp | 8 ++ include/boost/pfr/core_name.hpp | 56 +++++++++++++ include/boost/pfr/detail/core_name.hpp | 79 +++++++++++++++++++ include/boost/pfr/detail/stdarray.hpp | 30 +++++++ test/Jamfile | 3 +- test/config/print_config.cpp | 1 + test/core_name/Jamfile.v2 | 56 +++++++++++++ .../cxx20_nontype_template_args_detection.cpp | 23 ++++++ test/core_name/fields_names.cpp | 74 +++++++++++++++++ test/core_name/fields_names_constexpr.cpp | 40 ++++++++++ 12 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 include/boost/pfr/core_name.hpp create mode 100644 include/boost/pfr/detail/core_name.hpp create mode 100644 include/boost/pfr/detail/stdarray.hpp create mode 100644 test/core_name/Jamfile.v2 create mode 100644 test/core_name/cxx20_nontype_template_args_detection.cpp create mode 100644 test/core_name/fields_names.cpp create mode 100644 test/core_name/fields_names_constexpr.cpp diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 6a25c57..2939e53 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -181,8 +181,10 @@ 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::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 @@ -485,6 +487,7 @@ 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_ENABLE_GETTING_NAMES*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_ENABLE_GETTING_NAMES macro equal to 0. Defining this macro as 0 before including the header disables the ability to get a field's name. ]] [[*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. ]] ] diff --git a/include/boost/pfr.hpp b/include/boost/pfr.hpp index 5eb5085..8194ef4 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 c5ae2bd..48d97f6 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -98,6 +98,14 @@ # endif #endif +#ifndef BOOST_PFR_ENABLE_GETTING_NAMES +# if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911 +# define BOOST_PFR_ENABLE_GETTING_NAMES 1 +# else +# define BOOST_PFR_ENABLE_GETTING_NAMES 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 0000000..314847b --- /dev/null +++ b/include/boost/pfr/core_name.hpp @@ -0,0 +1,56 @@ +// 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 +#include + +#include + +namespace boost { namespace pfr { + +template +constexpr std::string_view get_name() noexcept { + static_assert(sizeof(T) && BOOST_PFR_ENABLE_GETTING_NAMES, "====================> Boost.PFR: Calling boost::pfr::get_name is allowed only in C++20"); + return detail::sequence_tuple::get( detail::tie_as_names_tuple() ); +} + +// FIXME: implement this +// template +// constexpr std::string_view get_name() noexcept { +// static_assert(sizeof(T) && BOOST_PFR_ENABLE_GETTING_NAMES, "====================> Boost.PFR: Calling boost::pfr::get_name is allowed only in C++20"); +// return detail::sequence_tuple::get_by_type_impl( detail::tie_as_names_tuple() ); +// } + +template +constexpr auto names_as_array() noexcept { + static_assert(sizeof(T) && BOOST_PFR_ENABLE_GETTING_NAMES, "====================> Boost.PFR: Calling boost::pfr::names_as_array is allowed only in C++20"); + return detail::make_stdarray_from_tietuple( + detail::tie_as_names_tuple(), + detail::make_index_sequence< tuple_size_v >() + ); +} + + +}} + +#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 0000000..703915b --- /dev/null +++ b/include/boost/pfr/detail/core_name.hpp @@ -0,0 +1,79 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace pfr { namespace detail { + +// TODO: move it outside +template +constexpr auto make_sequence_tuple(Args... args) noexcept { + return sequence_tuple::tuple{ args... }; +} + +template +constexpr auto name_of_field_impl() noexcept { +#ifdef _MSC_VER + constexpr std::string_view sv = __FUNCSIG__; + constexpr auto last = sv.find_last_not_of(" >(", sv.size() - 6); +#else + constexpr std::string_view sv = __PRETTY_FUNCTION__; + constexpr auto last = sv.find_last_not_of(" ])"); +#endif + constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last); + std::array res{}; + auto it = res.data(); + for (auto a = first+1; a <= last; ++a) + *it++ = sv[a]; + return res; +} + +template +extern const T fake_object; + +template +constexpr auto stored_name_of_field = name_of_field_impl( + detail::tie_as_tuple(fake_object) +)>(); + +template +constexpr auto tie_as_names_tuple_impl(std::index_sequence) noexcept { + return detail::make_sequence_tuple(std::string_view{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 tie_as_names_tuple_impl(detail::make_index_sequence()>{}); +} + +}}} + +#endif // BOOST_PFR_DETAIL_CORE_NAME_HPP + diff --git a/include/boost/pfr/detail/stdarray.hpp b/include/boost/pfr/detail/stdarray.hpp new file mode 100644 index 0000000..e375136 --- /dev/null +++ b/include/boost/pfr/detail/stdarray.hpp @@ -0,0 +1,30 @@ + +// 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 + +namespace boost { namespace pfr { namespace detail { + +template +constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence) noexcept { + return std::array{ + boost::pfr::detail::sequence_tuple::get(t)... + }; +} + +}}} + +#endif // BOOST_PFR_DETAIL_STDARRAY_HPP + diff --git a/test/Jamfile b/test/Jamfile index de6f4c0..2d7c089 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 e6c2905..7eff103 100644 --- a/test/config/print_config.cpp +++ b/test/config/print_config.cpp @@ -14,6 +14,7 @@ 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_ENABLE_GETTING_NAMES == " << BOOST_PFR_ENABLE_GETTING_NAMES << '\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 0000000..5f1f128 --- /dev/null +++ b/test/core_name/Jamfile.v2 @@ -0,0 +1,56 @@ +# 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 python ; +import testing ; +import ../../config/checks/config : requires ; + +########## BEGIN of helpers to detect C++20 non-type template args 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_nontype_template_args_detection.cpp : : : : compiler_supports_cxx20_nontype_template_args ; +explicit compiler_supports_cxx20_nontype_template_args ; + +########## END of helpers to detect C++20 non-type template args support + + +local REQUIRE_CXX20_NONTYPE_TEMPLATE_ARGS = + [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_template_args : : no ] + ; + +project + : source-location . + : requirements + BOOST_PFR_DETAIL_STRICT_RVALUE_TESTING=1 + [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_template_args : : no ] + ; + + + +test-suite pfr_tests + : + [ run fields_names.cpp : : : : ] + [ run fields_names_constexpr.cpp : : : : ] + ; + + diff --git a/test/core_name/cxx20_nontype_template_args_detection.cpp b/test/core_name/cxx20_nontype_template_args_detection.cpp new file mode 100644 index 0000000..e045575 --- /dev/null +++ b/test/core_name/cxx20_nontype_template_args_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/fields_names.cpp b/test/core_name/fields_names.cpp new file mode 100644 index 0000000..084d43f --- /dev/null +++ b/test/core_name/fields_names.cpp @@ -0,0 +1,74 @@ +// 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 { + +namespace { + +struct nonconstexpr { + nonconstexpr() {}; +}; + +struct Aggregate { + int member1; + nonconstexpr this_is_a_name; + std::reference_wrapper c; + std::string Forth; +}; + +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"); +} + +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 + +#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"); + +static_assert(boost::pfr::names_as_array() == std::array{ + "member1", + "this_is_a_name", + "c", + "Forth" + }); + +int main() {} + From f4ebd9d49d640b1bf618ea072c3e5b195f7e4fc3 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 26 Jun 2023 17:49:08 +0000 Subject: [PATCH 02/30] fix for cxx14 build --- include/boost/pfr/core_name.hpp | 8 +- include/boost/pfr/detail/core_name.hpp | 66 ++-------------- .../boost/pfr/detail/core_name14_disabled.hpp | 40 ++++++++++ .../boost/pfr/detail/core_name20_static.hpp | 79 +++++++++++++++++++ include/boost/pfr/detail/stdarray.hpp | 11 ++- 5 files changed, 137 insertions(+), 67 deletions(-) create mode 100644 include/boost/pfr/detail/core_name14_disabled.hpp create mode 100644 include/boost/pfr/detail/core_name20_static.hpp diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index 314847b..a4f1c65 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -21,28 +21,24 @@ #include #include -#include #include namespace boost { namespace pfr { template -constexpr std::string_view get_name() noexcept { - static_assert(sizeof(T) && BOOST_PFR_ENABLE_GETTING_NAMES, "====================> Boost.PFR: Calling boost::pfr::get_name is allowed only in C++20"); +constexpr auto get_name() noexcept { return detail::sequence_tuple::get( detail::tie_as_names_tuple() ); } // FIXME: implement this // template -// constexpr std::string_view get_name() noexcept { -// static_assert(sizeof(T) && BOOST_PFR_ENABLE_GETTING_NAMES, "====================> Boost.PFR: Calling boost::pfr::get_name is allowed only in C++20"); +// constexpr auto get_name() noexcept { // return detail::sequence_tuple::get_by_type_impl( detail::tie_as_names_tuple() ); // } template constexpr auto names_as_array() noexcept { - static_assert(sizeof(T) && BOOST_PFR_ENABLE_GETTING_NAMES, "====================> Boost.PFR: Calling boost::pfr::names_as_array is allowed only in C++20"); return detail::make_stdarray_from_tietuple( detail::tie_as_names_tuple(), detail::make_index_sequence< tuple_size_v >() diff --git a/include/boost/pfr/detail/core_name.hpp b/include/boost/pfr/detail/core_name.hpp index 703915b..9878ce3 100644 --- a/include/boost/pfr/detail/core_name.hpp +++ b/include/boost/pfr/detail/core_name.hpp @@ -13,67 +13,17 @@ #pragma once #include -#include -#include -#include -#include -#include -#include -#include -namespace boost { namespace pfr { namespace detail { - -// TODO: move it outside -template -constexpr auto make_sequence_tuple(Args... args) noexcept { - return sequence_tuple::tuple{ args... }; -} - -template -constexpr auto name_of_field_impl() noexcept { -#ifdef _MSC_VER - constexpr std::string_view sv = __FUNCSIG__; - constexpr auto last = sv.find_last_not_of(" >(", sv.size() - 6); +// Each core_name provides `TODO:` and +// `TODO:` functions. +// +// The whole functional of extracting field's names is build on top of those +// two functions. +#if BOOST_PFR_ENABLE_GETTING_NAMES +#include #else - constexpr std::string_view sv = __PRETTY_FUNCTION__; - constexpr auto last = sv.find_last_not_of(" ])"); +#include #endif - constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last); - std::array res{}; - auto it = res.data(); - for (auto a = first+1; a <= last; ++a) - *it++ = sv[a]; - return res; -} - -template -extern const T fake_object; - -template -constexpr auto stored_name_of_field = name_of_field_impl( - detail::tie_as_tuple(fake_object) -)>(); - -template -constexpr auto tie_as_names_tuple_impl(std::index_sequence) noexcept { - return detail::make_sequence_tuple(std::string_view{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 tie_as_names_tuple_impl(detail::make_index_sequence()>{}); -} - -}}} #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 0000000..1e6d39c --- /dev/null +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -0,0 +1,40 @@ +// 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 { + +// TODO: move it outside +template +constexpr auto make_sequence_tuple(Args... args) noexcept { + return sequence_tuple::tuple{ args... }; +} + + +template +constexpr auto tie_as_names_tuple() noexcept { + static_assert( + sizeof(T) && false, + "====================> Boost.PFR: Extraction of field's names is allowed only in C++20" + ); + + return detail::make_sequence_tuple(); +} + +}}} + +#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 0000000..0000371 --- /dev/null +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -0,0 +1,79 @@ +// 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 + +namespace boost { namespace pfr { namespace detail { + +// TODO: move it outside +template +constexpr auto make_sequence_tuple(Args... args) noexcept { + return sequence_tuple::tuple{ args... }; +} + +template +constexpr auto name_of_field_impl() noexcept { +#ifdef _MSC_VER + constexpr std::string_view sv = __FUNCSIG__; + constexpr auto last = sv.find_last_not_of(" >(", sv.size() - 6); +#else + constexpr std::string_view sv = __PRETTY_FUNCTION__; + constexpr auto last = sv.find_last_not_of(" ])"); +#endif + constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last); + std::array res{}; + auto it = res.data(); + for (auto a = first+1; a <= last; ++a) + *it++ = sv[a]; + return res; +} + +template +extern const T fake_object; + +template +constexpr auto stored_name_of_field = name_of_field_impl( + detail::tie_as_tuple(fake_object) +)>(); + +template +constexpr auto tie_as_names_tuple_impl(std::index_sequence) noexcept { + return detail::make_sequence_tuple(std::string_view{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 tie_as_names_tuple_impl(detail::make_index_sequence()>{}); +} + +}}} + +#endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP + diff --git a/include/boost/pfr/detail/stdarray.hpp b/include/boost/pfr/detail/stdarray.hpp index e375136..01e860b 100644 --- a/include/boost/pfr/detail/stdarray.hpp +++ b/include/boost/pfr/detail/stdarray.hpp @@ -1,4 +1,3 @@ - // Copyright (c) 2023 Denis Mikhailov // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -12,16 +11,22 @@ #include // metaprogramming stuff #include +#include // for std::common_type_t #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) noexcept { - return std::array{ + return make_stdarray( boost::pfr::detail::sequence_tuple::get(t)... - }; + ); } }}} From 86911e0247004302b9c13946e43ce74321f53743 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Tue, 27 Jun 2023 12:39:47 +0000 Subject: [PATCH 03/30] fix lint issues --- include/boost/pfr/core_name.hpp | 4 ++-- include/boost/pfr/detail/core_name14_disabled.hpp | 2 +- include/boost/pfr/detail/core_name20_static.hpp | 2 +- test/config/print_config.cpp | 2 +- test/core_name/fields_names.cpp | 6 +++--- test/core_name/fields_names_constexpr.cpp | 10 +++++----- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index a4f1c65..fad190b 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -40,8 +40,8 @@ constexpr auto get_name() noexcept { template constexpr auto names_as_array() noexcept { return detail::make_stdarray_from_tietuple( - detail::tie_as_names_tuple(), - detail::make_index_sequence< tuple_size_v >() + detail::tie_as_names_tuple(), + detail::make_index_sequence< tuple_size_v >() ); } diff --git a/include/boost/pfr/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index 1e6d39c..7d1c9f3 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -28,7 +28,7 @@ template constexpr auto tie_as_names_tuple() noexcept { static_assert( sizeof(T) && false, - "====================> Boost.PFR: Extraction of field's names is allowed only in C++20" + "====================> Boost.PFR: Extraction of field's names is allowed only in C++20" ); return detail::make_sequence_tuple(); diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 0000371..574f961 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -67,7 +67,7 @@ constexpr auto tie_as_names_tuple() noexcept { ); 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." + "====================> Boost.PFR: Extraction of field's names is allowed only when the BOOST_PFR_USE_CPP17 macro enabled." ); return tie_as_names_tuple_impl(detail::make_index_sequence()>{}); diff --git a/test/config/print_config.cpp b/test/config/print_config.cpp index 7eff103..78e0e1b 100644 --- a/test/config/print_config.cpp +++ b/test/config/print_config.cpp @@ -14,7 +14,7 @@ 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_ENABLE_GETTING_NAMES == " << BOOST_PFR_ENABLE_GETTING_NAMES << '\n' + << "BOOST_PFR_ENABLE_GETTING_NAMES == " << BOOST_PFR_ENABLE_GETTING_NAMES << '\n' << "BOOST_PFR_ENABLED == " << BOOST_PFR_ENABLED << '\n' << "__cplusplus == " << __cplusplus << '\n' #ifdef __cpp_structured_bindings diff --git a/test/core_name/fields_names.cpp b/test/core_name/fields_names.cpp index 084d43f..30e4fe4 100644 --- a/test/core_name/fields_names.cpp +++ b/test/core_name/fields_names.cpp @@ -47,9 +47,9 @@ void test_get_name_by_type() { void test_names_as_array() { const auto expected = std::array{ "member1", - "this_is_a_name", - "c", - "Forth" + "this_is_a_name", + "c", + "Forth" }; const auto value = boost::pfr::names_as_array(); BOOST_TEST_EQ(expected.size(), value.size()); diff --git a/test/core_name/fields_names_constexpr.cpp b/test/core_name/fields_names_constexpr.cpp index fb69ea5..8b3ebef 100644 --- a/test/core_name/fields_names_constexpr.cpp +++ b/test/core_name/fields_names_constexpr.cpp @@ -30,11 +30,11 @@ static_assert(boost::pfr::get_name<2, Aggregate>() == "c"); static_assert(boost::pfr::get_name<3, Aggregate>() == "Forth"); static_assert(boost::pfr::names_as_array() == std::array{ - "member1", - "this_is_a_name", - "c", - "Forth" - }); + "member1", + "this_is_a_name", + "c", + "Forth" +}); int main() {} From 04aef42dcbea8a6a690edea3e06c423440bc74ff Mon Sep 17 00:00:00 2001 From: denzor200 Date: Wed, 28 Jun 2023 12:32:26 +0000 Subject: [PATCH 04/30] review --- include/boost/pfr/config.hpp | 10 +++- include/boost/pfr/core_name.hpp | 2 +- include/boost/pfr/detail/core_name.hpp | 4 +- .../boost/pfr/detail/core_name14_disabled.hpp | 11 +++- .../boost/pfr/detail/core_name20_static.hpp | 27 +++++++-- test/core_name/Jamfile.v2 | 2 + test/core_name/fields_names.cpp | 1 + test/core_name/fields_names_nonascii.cpp | 50 +++++++++++++++++ .../core_name/fields_names_unnamed_struct.cpp | 56 +++++++++++++++++++ 9 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 test/core_name/fields_names_nonascii.cpp create mode 100644 test/core_name/fields_names_unnamed_struct.cpp diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 48d97f6..3c99048 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -98,8 +98,16 @@ # endif #endif +#ifndef BOOST_PFR_FUNCTION_MACRO_SUPPORTED +# if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) +# define BOOST_PFR_FUNCTION_MACRO_SUPPORTED 1 +# else +# define BOOST_PFR_FUNCTION_MACRO_SUPPORTED 0 +# endif +#endif + #ifndef BOOST_PFR_ENABLE_GETTING_NAMES -# if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911 +# if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911 && BOOST_PFR_FUNCTION_MACRO_SUPPORTED # define BOOST_PFR_ENABLE_GETTING_NAMES 1 # else # define BOOST_PFR_ENABLE_GETTING_NAMES 0 diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index fad190b..2112311 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -28,7 +28,7 @@ namespace boost { namespace pfr { template constexpr auto get_name() noexcept { - return detail::sequence_tuple::get( detail::tie_as_names_tuple() ); + return detail::get_name(); } // FIXME: implement this diff --git a/include/boost/pfr/detail/core_name.hpp b/include/boost/pfr/detail/core_name.hpp index 9878ce3..e5846c6 100644 --- a/include/boost/pfr/detail/core_name.hpp +++ b/include/boost/pfr/detail/core_name.hpp @@ -14,8 +14,8 @@ #include -// Each core_name provides `TODO:` and -// `TODO:` functions. +// 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. diff --git a/include/boost/pfr/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index 7d1c9f3..40cf5ce 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -23,12 +23,21 @@ constexpr auto make_sequence_tuple(Args... args) noexcept { return sequence_tuple::tuple{ args... }; } +template +constexpr auto get_name() noexcept { + static_assert( + sizeof(T) && false, + "====================> Boost.PFR: Field's names extracting functionality requires C++20 and compiler that supports __PRETTY_FUNCTION__ or __FUNCSIG__ macro (GCC, Clang or MSVC)." + ); + + return nullptr; +} template constexpr auto tie_as_names_tuple() noexcept { static_assert( sizeof(T) && false, - "====================> Boost.PFR: Extraction of field's names is allowed only in C++20" + "====================> Boost.PFR: Field's names extracting functionality requires C++20 and compiler that supports __PRETTY_FUNCTION__ or __FUNCSIG__ macro (GCC, Clang or MSVC)." ); return detail::make_sequence_tuple(); diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 574f961..4a061ad 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -20,6 +20,7 @@ #include #include #include +#include // for std::ranges::copy namespace boost { namespace pfr { namespace detail { @@ -30,7 +31,7 @@ constexpr auto make_sequence_tuple(Args... args) noexcept { } template -constexpr auto name_of_field_impl() noexcept { +consteval auto name_of_field_impl() noexcept { #ifdef _MSC_VER constexpr std::string_view sv = __FUNCSIG__; constexpr auto last = sv.find_last_not_of(" >(", sv.size() - 6); @@ -38,11 +39,11 @@ constexpr auto name_of_field_impl() noexcept { constexpr std::string_view sv = __PRETTY_FUNCTION__; constexpr auto last = sv.find_last_not_of(" ])"); #endif - constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last); - std::array res{}; - auto it = res.data(); - for (auto a = first+1; a <= last; ++a) - *it++ = sv[a]; + constexpr auto first = sv.find_last_of(":", last); + auto res = std::array{}; + std::ranges::copy(sv.begin()+first+1, + sv.begin()+last+1, + res.begin()); return res; } @@ -59,6 +60,20 @@ constexpr auto tie_as_names_tuple_impl(std::index_sequence) noexcept { return detail::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( diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index 5f1f128..a64053c 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -51,6 +51,8 @@ test-suite pfr_tests : [ run fields_names.cpp : : : : ] [ run fields_names_constexpr.cpp : : : : ] + [ run fields_names_nonascii.cpp : : : : ] + [ run fields_names_unnamed_struct.cpp : : : "-fpermissive" : ] ; diff --git a/test/core_name/fields_names.cpp b/test/core_name/fields_names.cpp index 30e4fe4..55e2746 100644 --- a/test/core_name/fields_names.cpp +++ b/test/core_name/fields_names.cpp @@ -58,6 +58,7 @@ void test_names_as_array() { } } + } // anonymous namespace diff --git a/test/core_name/fields_names_nonascii.cpp b/test/core_name/fields_names_nonascii.cpp new file mode 100644 index 0000000..cda338c --- /dev/null +++ b/test/core_name/fields_names_nonascii.cpp @@ -0,0 +1,50 @@ +// 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 + +namespace testing { + +namespace { + +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 + +#include + +namespace testing { + +namespace { + +struct { + int field_of_unnamed_structure; +} unnamed; + +void test_get_name() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, decltype(unnamed)>())), "field_of_unnamed_structure"); +} + +void test_names_as_array() { + const auto expected = std::array{ + "field_of_unnamed_structure" + }; + const auto value = boost::pfr::names_as_array(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i())), "a_field_name" ); +} + +} // anonymous namespace + + +} // namespace testing + +int main() { + testing::test_get_name(); + testing::test_names_as_array(); + testing::test_get_name_for_local_structure(); + + return boost::report_errors(); +} + + From 282e033e4bf98ed33729bbf7bc9849e7a5828bf5 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 12 Aug 2023 17:47:58 +0000 Subject: [PATCH 05/30] Fix lint issue about nonascii symbol --- test/core_name/Jamfile.v2 | 11 ++++++++++- ...mes_nonascii.cpp => fields_names_nonascii.cpp.tpl} | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) rename test/core_name/{fields_names_nonascii.cpp => fields_names_nonascii.cpp.tpl} (90%) diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index a64053c..e77c04a 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -33,7 +33,6 @@ explicit compiler_supports_cxx20_nontype_template_args ; ########## END of helpers to detect C++20 non-type template args support - local REQUIRE_CXX20_NONTYPE_TEMPLATE_ARGS = [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_template_args : : no ] ; @@ -46,6 +45,16 @@ project ; +########## BEGIN of helpers to prepare the test with non-ascii field name + +actions insert_utf8_action +{ + sed 's/%ARG%/\xcf\x80/' $(>) > $(<) +} + +make fields_names_nonascii.cpp : fields_names_nonascii.cpp.tpl : @insert_utf8_action ; + +########## END of helpers to prepare the test with non-ascii field name test-suite pfr_tests : diff --git a/test/core_name/fields_names_nonascii.cpp b/test/core_name/fields_names_nonascii.cpp.tpl similarity index 90% rename from test/core_name/fields_names_nonascii.cpp rename to test/core_name/fields_names_nonascii.cpp.tpl index cda338c..2e83aac 100644 --- a/test/core_name/fields_names_nonascii.cpp +++ b/test/core_name/fields_names_nonascii.cpp.tpl @@ -17,16 +17,16 @@ namespace testing { namespace { struct Aggregate { - int _π; + int _%ARG%; }; void test_get_name() { - BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate>())), "_π"); + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate>())), "_%ARG%"); } void test_names_as_array() { const auto expected = std::array{ - "_π" + "_%ARG%" }; const auto value = boost::pfr::names_as_array(); BOOST_TEST_EQ(expected.size(), value.size()); From 356e937dd902ab181670d0da003fc669c4ea3515 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 12 Aug 2023 19:28:42 +0000 Subject: [PATCH 06/30] Fix strip_boost_namespace.sh --- .github/workflows/ci.yml | 4 ++-- include/boost/pfr/core_name.hpp | 2 +- include/boost/pfr/detail/core_name14_disabled.hpp | 2 +- include/boost/pfr/detail/core_name20_static.hpp | 2 +- include/boost/pfr/detail/stdarray.hpp | 2 +- misc/strip_boost_namespace.sh | 12 +++++++++--- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53f77f1..c7b41ea 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/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index 2112311..e5feb55 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -46,7 +46,7 @@ constexpr auto names_as_array() noexcept { } -}} +}} // namespace boost::pfr #endif // BOOST_PFR_CORE_NAME_HPP diff --git a/include/boost/pfr/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index 40cf5ce..b9271e5 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -43,7 +43,7 @@ constexpr auto tie_as_names_tuple() noexcept { return detail::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 index 4a061ad..2fd5b0d 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -88,7 +88,7 @@ constexpr auto tie_as_names_tuple() noexcept { return 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/stdarray.hpp b/include/boost/pfr/detail/stdarray.hpp index 01e860b..3c09eed 100644 --- a/include/boost/pfr/detail/stdarray.hpp +++ b/include/boost/pfr/detail/stdarray.hpp @@ -29,7 +29,7 @@ constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence ); } -}}} +}}} // namespace boost::pfr::detail #endif // BOOST_PFR_DETAIL_STDARRAY_HPP diff --git a/misc/strip_boost_namespace.sh b/misc/strip_boost_namespace.sh index 9134ca1..28cc3a0 100755 --- a/misc/strip_boost_namespace.sh +++ b/misc/strip_boost_namespace.sh @@ -39,20 +39,26 @@ find ${TARGET_PATH}/doc -type f | xargs sed -i 's|Boost.PFR|PFR|g' sed -i 's|# \[Boost.PFR\](https://boost.org/libs/pfr)|# [PFR](https://apolukhin.github.io/pfr_non_boost/)|g' ${TARGET_PATH}/README.md echo -n "***** Testing: " -if g++-9 -std=c++17 -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=1 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /dev/null; then +if g++-12 -std=c++2a -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=1 -DPFR_ENABLE_GETTING_NAMES=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 +if g++-12 -std=c++2a -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=1 -DPFR_ENABLE_GETTING_NAMES=0 -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++-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 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_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" From 3438d5e81585a3a179e8e742959dbde7dbbb167a Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 13 Aug 2023 22:01:19 +0000 Subject: [PATCH 07/30] Fix MSVC --- doc/pfr.qbk | 20 +++++++ .../boost/pfr/detail/core_name20_static.hpp | 12 ++-- test/core_name/Jamfile.v2 | 14 ++--- test/core_name/fields_names.cpp | 8 +++ .../core_name/fields_names_unnamed_struct.cpp | 56 ------------------- 5 files changed, 42 insertions(+), 68 deletions(-) delete mode 100644 test/core_name/fields_names_unnamed_struct.cpp diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 2939e53..27d94af 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -477,6 +477,26 @@ 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.PFR's 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 or local structure. + +``` +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. + [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: diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 2fd5b0d..6e696fb 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -30,17 +30,18 @@ constexpr auto make_sequence_tuple(Args... args) noexcept { return sequence_tuple::tuple{ args... }; } -template +template consteval auto name_of_field_impl() noexcept { #ifdef _MSC_VER constexpr std::string_view sv = __FUNCSIG__; - constexpr auto last = sv.find_last_not_of(" >(", sv.size() - 6); + // - strlen("(void)") - strlen(" noexcept") + constexpr auto last = sv.find_last_not_of(" >(", sv.size() - 6 - 9); #else constexpr std::string_view sv = __PRETTY_FUNCTION__; constexpr auto last = sv.find_last_not_of(" ])"); #endif - constexpr auto first = sv.find_last_of(":", last); - auto res = std::array{}; + constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last); + auto res = std::array{}; std::ranges::copy(sv.begin()+first+1, sv.begin()+last+1, res.begin()); @@ -50,8 +51,9 @@ consteval auto name_of_field_impl() noexcept { template extern const T fake_object; +// Without passing 'T' into 'name_of_field_impl' different fields from different structures might have the same name! template -constexpr auto stored_name_of_field = name_of_field_impl( +constexpr auto stored_name_of_field = name_of_field_impl( detail::tie_as_tuple(fake_object) )>(); diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index e77c04a..bce6186 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -47,12 +47,13 @@ project ########## BEGIN of helpers to prepare the test with non-ascii field name -actions insert_utf8_action -{ - sed 's/%ARG%/\xcf\x80/' $(>) > $(<) -} +# TODO: fix it +# actions insert_utf8_action +# { +# sed 's/%ARG%/\xcf\x80/' $(>) > $(<) +# } -make fields_names_nonascii.cpp : fields_names_nonascii.cpp.tpl : @insert_utf8_action ; +# make fields_names_nonascii.cpp : fields_names_nonascii.cpp.tpl : @insert_utf8_action ; ########## END of helpers to prepare the test with non-ascii field name @@ -60,8 +61,7 @@ test-suite pfr_tests : [ run fields_names.cpp : : : : ] [ run fields_names_constexpr.cpp : : : : ] - [ run fields_names_nonascii.cpp : : : : ] - [ run fields_names_unnamed_struct.cpp : : : "-fpermissive" : ] + # [ run fields_names_nonascii.cpp : : : : ] ; diff --git a/test/core_name/fields_names.cpp b/test/core_name/fields_names.cpp index 55e2746..a122529 100644 --- a/test/core_name/fields_names.cpp +++ b/test/core_name/fields_names.cpp @@ -29,11 +29,19 @@ struct Aggregate { std::string Forth; }; +struct A { + int first; + int second; +}; + 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() { diff --git a/test/core_name/fields_names_unnamed_struct.cpp b/test/core_name/fields_names_unnamed_struct.cpp deleted file mode 100644 index ba77dd3..0000000 --- a/test/core_name/fields_names_unnamed_struct.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// 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 - -namespace testing { - -namespace { - -struct { - int field_of_unnamed_structure; -} unnamed; - -void test_get_name() { - BOOST_TEST_EQ( ((boost::pfr::get_name<0, decltype(unnamed)>())), "field_of_unnamed_structure"); -} - -void test_names_as_array() { - const auto expected = std::array{ - "field_of_unnamed_structure" - }; - const auto value = boost::pfr::names_as_array(); - BOOST_TEST_EQ(expected.size(), value.size()); - for (std::size_t i=0;i())), "a_field_name" ); -} - -} // anonymous namespace - - -} // namespace testing - -int main() { - testing::test_get_name(); - testing::test_names_as_array(); - testing::test_get_name_for_local_structure(); - - return boost::report_errors(); -} - - From ad7ab1cfc3aa5f754cdbc0992b1a0da6bb78c8a2 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 19 Aug 2023 17:13:29 +0300 Subject: [PATCH 08/30] Add Clang support --- doc/pfr.qbk | 2 +- include/boost/pfr/config.hpp | 8 +++- .../boost/pfr/detail/core_name20_static.hpp | 39 ++++++++++++++++--- test/core_name/Jamfile.v2 | 11 +++--- .../cxx20_clang_workaround_detection.cpp | 32 +++++++++++++++ test/core_name/fields_names.cpp | 5 --- test/core_name/fields_names_nonascii.cpp.tpl | 4 -- 7 files changed, 78 insertions(+), 23 deletions(-) create mode 100644 test/core_name/cxx20_clang_workaround_detection.cpp diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 27d94af..e11523f 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -478,7 +478,7 @@ struct aggregate : empty { // not a SimpleAggregate The library may work with aggregates that don't satisfy the requirements of `SimpleAggregate`, but the behavior tends to be non-portable. Boost.PFR's 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 or local structure. +It's better not to use this feature with anonymous structure, local structure or a structure defined inside anonymous namespace. ``` struct external_simple_aggregate { diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 3c99048..73d4bed 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -107,8 +107,12 @@ #endif #ifndef BOOST_PFR_ENABLE_GETTING_NAMES -# if defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911 && BOOST_PFR_FUNCTION_MACRO_SUPPORTED -# define BOOST_PFR_ENABLE_GETTING_NAMES 1 +# if (defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911) || (__cplusplus >= 202002L && defined(__clang_major__) && __clang_major__ >= 12) +# if BOOST_PFR_FUNCTION_MACRO_SUPPORTED +# define BOOST_PFR_ENABLE_GETTING_NAMES 1 +# else +# define BOOST_PFR_ENABLE_GETTING_NAMES 0 +# endif # else # define BOOST_PFR_ENABLE_GETTING_NAMES 0 # endif diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 6e696fb..8954315 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -30,15 +30,15 @@ constexpr auto make_sequence_tuple(Args... args) noexcept { return sequence_tuple::tuple{ args... }; } -template +template consteval auto name_of_field_impl() noexcept { #ifdef _MSC_VER constexpr std::string_view sv = __FUNCSIG__; // - strlen("(void)") - strlen(" noexcept") - constexpr auto last = sv.find_last_not_of(" >(", sv.size() - 6 - 9); + constexpr auto last = sv.find_last_not_of(" >(}", sv.size() - 6 - 9); #else constexpr std::string_view sv = __PRETTY_FUNCTION__; - constexpr auto last = sv.find_last_not_of(" ])"); + constexpr auto last = sv.find_last_not_of(" ])}"); #endif constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last); auto res = std::array{}; @@ -51,11 +51,40 @@ consteval auto name_of_field_impl() noexcept { template extern const T fake_object; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundefined-var-template" + +template +struct clang_workaround_t { + T v; +}; +template +clang_workaround_t(T) -> clang_workaround_t; + +template +constexpr auto clang_workaround(const T& arg) noexcept { + return clang_workaround_t{arg}; +} + +#else + +template +constexpr const T& clang_workaround(const T& arg) noexcept { + return arg; +} + +#endif + // Without passing 'T' into 'name_of_field_impl' different fields from different structures might have the same name! template -constexpr auto stored_name_of_field = name_of_field_impl( +constexpr auto stored_name_of_field = 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 { diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index bce6186..c787391 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -12,7 +12,7 @@ import python ; import testing ; import ../../config/checks/config : requires ; -########## BEGIN of helpers to detect C++20 non-type template args support +########## BEGIN of helpers to detect C++20 features support actions mp_simple_run_action { @@ -31,17 +31,16 @@ rule mp-run-simple ( sources + : args * : input-files * : requirements * : targe mp-run-simple cxx20_nontype_template_args_detection.cpp : : : : compiler_supports_cxx20_nontype_template_args ; explicit compiler_supports_cxx20_nontype_template_args ; -########## END of helpers to detect C++20 non-type template args support +mp-run-simple cxx20_clang_workaround_detection.cpp : : : : compiler_supports_cxx20_clang_workaround ; +explicit compiler_supports_cxx20_clang_workaround ; -local REQUIRE_CXX20_NONTYPE_TEMPLATE_ARGS = - [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_template_args : : no ] - ; +########## 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_nontype_template_args : : no ] + [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_template_args : : [ check-target-builds ../core_name//compiler_supports_cxx20_clang_workaround : : no ] ] ; diff --git a/test/core_name/cxx20_clang_workaround_detection.cpp b/test/core_name/cxx20_clang_workaround_detection.cpp new file mode 100644 index 0000000..277bd6c --- /dev/null +++ b/test/core_name/cxx20_clang_workaround_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/fields_names.cpp b/test/core_name/fields_names.cpp index a122529..d0a07bc 100644 --- a/test/core_name/fields_names.cpp +++ b/test/core_name/fields_names.cpp @@ -16,8 +16,6 @@ namespace testing { -namespace { - struct nonconstexpr { nonconstexpr() {}; }; @@ -67,9 +65,6 @@ void test_names_as_array() { } -} // anonymous namespace - - } // namespace testing int main() { diff --git a/test/core_name/fields_names_nonascii.cpp.tpl b/test/core_name/fields_names_nonascii.cpp.tpl index 2e83aac..972b064 100644 --- a/test/core_name/fields_names_nonascii.cpp.tpl +++ b/test/core_name/fields_names_nonascii.cpp.tpl @@ -14,8 +14,6 @@ namespace testing { -namespace { - struct Aggregate { int _%ARG%; }; @@ -35,8 +33,6 @@ void test_names_as_array() { } } -} // anonymous namespace - } // namespace testing From 642d1f7d23bd047be064d8d9282b8f758feb442e Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 20 Aug 2023 00:49:14 +0300 Subject: [PATCH 09/30] Fix nonascii fields --- .../boost/pfr/detail/core_name20_static.hpp | 2 +- test/core_name/Jamfile.v2 | 15 ++--- test/core_name/fields_names_nonascii.cpp.tpl | 46 ---------------- .../generate_fields_names_nonascii.cpp.py | 55 +++++++++++++++++++ 4 files changed, 62 insertions(+), 56 deletions(-) delete mode 100644 test/core_name/fields_names_nonascii.cpp.tpl create mode 100644 test/core_name/generate_fields_names_nonascii.cpp.py diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 8954315..7ab7a5a 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -40,7 +40,7 @@ consteval auto name_of_field_impl() noexcept { constexpr std::string_view sv = __PRETTY_FUNCTION__; constexpr auto last = sv.find_last_not_of(" ])}"); #endif - constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last); + constexpr auto first = sv.find_last_of(".:->", last); auto res = std::array{}; std::ranges::copy(sv.begin()+first+1, sv.begin()+last+1, diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index c787391..c95538e 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -44,23 +44,20 @@ project ; -########## BEGIN of helpers to prepare the test with non-ascii field name -# TODO: fix it -# actions insert_utf8_action -# { -# sed 's/%ARG%/\xcf\x80/' $(>) > $(<) -# } -# make fields_names_nonascii.cpp : fields_names_nonascii.cpp.tpl : @insert_utf8_action ; +actions invoke_python_generator +{ + python $(>) > $(<) +} -########## END of helpers to prepare the test with non-ascii field name +make fields_names_nonascii.cpp : generate_fields_names_nonascii.cpp.py : @invoke_python_generator ; test-suite pfr_tests : [ run fields_names.cpp : : : : ] [ run fields_names_constexpr.cpp : : : : ] - # [ run fields_names_nonascii.cpp : : : : ] + [ run fields_names_nonascii.cpp : : : msvc:"/utf-8" : ] ; diff --git a/test/core_name/fields_names_nonascii.cpp.tpl b/test/core_name/fields_names_nonascii.cpp.tpl deleted file mode 100644 index 972b064..0000000 --- a/test/core_name/fields_names_nonascii.cpp.tpl +++ /dev/null @@ -1,46 +0,0 @@ -// 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 - -namespace testing { - -struct Aggregate { - int _%ARG%; -}; - -void test_get_name() { - BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate>())), "_%ARG%"); -} - -void test_names_as_array() { - const auto expected = std::array{ - "_%ARG%" - }; - const auto value = boost::pfr::names_as_array(); - BOOST_TEST_EQ(expected.size(), value.size()); - for (std::size_t i=0;i + +#include + +namespace testing { + +struct Aggregate { + int _%ARG%; +}; + +void test_get_name() { + BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate>())), "_%ARG%"); +} + +void test_names_as_array() { + const auto expected = std::array{ + "_%ARG%" + }; + const auto value = boost::pfr::names_as_array(); + BOOST_TEST_EQ(expected.size(), value.size()); + for (std::size_t i=0;i Date: Fri, 25 Aug 2023 01:42:09 +0300 Subject: [PATCH 10/30] Add test for big structures --- test/core_name/Jamfile.v2 | 2 + .../generate_fields_names_big.cpp.py | 163 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 test/core_name/generate_fields_names_big.cpp.py diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index c95538e..238f94f 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -52,12 +52,14 @@ actions invoke_python_generator } make fields_names_nonascii.cpp : generate_fields_names_nonascii.cpp.py : @invoke_python_generator ; +make fields_names_big.cpp : generate_fields_names_big.cpp.py : @invoke_python_generator ; test-suite pfr_tests : [ run fields_names.cpp : : : : ] [ run fields_names_constexpr.cpp : : : : ] [ run fields_names_nonascii.cpp : : : msvc:"/utf-8" : ] + [ run fields_names_big.cpp : : : msvc:"/bigobj" : ] ; diff --git a/test/core_name/generate_fields_names_big.cpp.py b/test/core_name/generate_fields_names_big.cpp.py new file mode 100644 index 0000000..30f7fc8 --- /dev/null +++ b/test/core_name/generate_fields_names_big.cpp.py @@ -0,0 +1,163 @@ +# 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 = """#include +#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 Date: Sat, 26 Aug 2023 22:31:38 +0300 Subject: [PATCH 11/30] Refactoring for parser --- .../boost/pfr/detail/core_name20_static.hpp | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 7ab7a5a..0017882 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -30,21 +30,32 @@ constexpr auto make_sequence_tuple(Args... args) noexcept { return sequence_tuple::tuple{ args... }; } +consteval std::string_view name_of_field_parse(std::string_view sv, + std::size_t size_at_begin, + std::size_t size_at_end, + std::string_view until_runtime_last) noexcept { + sv.remove_prefix(size_at_begin); + sv.remove_suffix(size_at_end); + return sv.substr(sv.rfind(until_runtime_last) + until_runtime_last.size()); +} + template consteval auto name_of_field_impl() noexcept { #ifdef _MSC_VER - constexpr std::string_view sv = __FUNCSIG__; - // - strlen("(void)") - strlen(" noexcept") - constexpr auto last = sv.find_last_not_of(" >(}", sv.size() - 6 - 9); -#else - constexpr std::string_view sv = __PRETTY_FUNCTION__; - constexpr auto last = sv.find_last_not_of(" ])}"); + constexpr auto sv = __FUNCSIG__; + // sizeof("auto __cdecl boost::pfr::detail::name_of_field_impl<") - 1, sizeof(">(void) noexcept") - 1 + constexpr auto fn = detail::name_of_field_parse(sv, 52, 16, "->"); +#elif __clang__ + constexpr auto sv = __PRETTY_FUNCTION__; + // sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1 + constexpr auto fn = detail::name_of_field_parse(sv, 64, 2, "."); +#else // GCC + constexpr auto sv = __PRETTY_FUNCTION__; + // sizeof("consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = ") - 1, sizeof(")]") - 1 + constexpr auto fn = detail::name_of_field_parse(sv, 79, 2, "::"); #endif - constexpr auto first = sv.find_last_of(".:->", last); - auto res = std::array{}; - std::ranges::copy(sv.begin()+first+1, - sv.begin()+last+1, - res.begin()); + auto res = std::array{}; + std::ranges::copy(fn, res.begin()); return res; } From 65bf6b579ff256aee006e0d3c3098678f5bb03d3 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 27 Aug 2023 00:41:00 +0300 Subject: [PATCH 12/30] review --- doc/pfr.qbk | 2 +- include/boost/pfr/config.hpp | 19 +--- include/boost/pfr/core_name.hpp | 4 +- include/boost/pfr/detail/core_name.hpp | 2 +- .../boost/pfr/detail/core_name14_disabled.hpp | 10 +- .../boost/pfr/detail/core_name20_static.hpp | 16 +-- include/boost/pfr/detail/sequence_tuple.hpp | 4 + include/boost/pfr/detail/stdarray.hpp | 2 +- misc/strip_boost_namespace.sh | 10 +- test/config/print_config.cpp | 2 +- test/core_name/fields_names.cpp | 4 +- test/core_name/fields_names_constexpr.cpp | 4 +- .../generate_fields_names_big.cpp.py | 104 +++++++++++++++++- 13 files changed, 136 insertions(+), 47 deletions(-) diff --git a/doc/pfr.qbk b/doc/pfr.qbk index e11523f..d810eac 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -507,7 +507,7 @@ 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_ENABLE_GETTING_NAMES*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_ENABLE_GETTING_NAMES macro equal to 0. Defining this macro as 0 before including the header disables the ability to get a field's name. ]] + [[*BOOST_PFR_ENABLE_GET_NAME_STATIC*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_ENABLE_GET_NAME_STATIC macro equal to 0. Defining this macro as 0 before including the header disables the ability to get a field's name. ]] [[*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. ]] ] diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 73d4bed..8c24982 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -98,23 +98,16 @@ # endif #endif -#ifndef BOOST_PFR_FUNCTION_MACRO_SUPPORTED -# if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) -# define BOOST_PFR_FUNCTION_MACRO_SUPPORTED 1 -# else -# define BOOST_PFR_FUNCTION_MACRO_SUPPORTED 0 -# endif -#endif - -#ifndef BOOST_PFR_ENABLE_GETTING_NAMES +#ifndef BOOST_PFR_ENABLE_GET_NAME_STATIC # if (defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911) || (__cplusplus >= 202002L && defined(__clang_major__) && __clang_major__ >= 12) -# if BOOST_PFR_FUNCTION_MACRO_SUPPORTED -# define BOOST_PFR_ENABLE_GETTING_NAMES 1 +// Only these 3 compilers have a macro to extract func name +# if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) +# define BOOST_PFR_ENABLE_GET_NAME_STATIC 1 # else -# define BOOST_PFR_ENABLE_GETTING_NAMES 0 +# define BOOST_PFR_ENABLE_GET_NAME_STATIC 0 # endif # else -# define BOOST_PFR_ENABLE_GETTING_NAMES 0 +# define BOOST_PFR_ENABLE_GET_NAME_STATIC 0 # endif #endif diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index e5feb55..d2571ca 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -9,7 +9,7 @@ // #ifndef BOOST_PFR_CORE_NAME_HPP -#define BOOST_PFR_CORE_NAME_HPP +#define BOOST_PFR_CORE_NAME_HPP #pragma once #include @@ -48,5 +48,5 @@ constexpr auto names_as_array() noexcept { }} // namespace boost::pfr -#endif // BOOST_PFR_CORE_NAME_HPP +#endif // BOOST_PFR_CORE_NAME_HPP diff --git a/include/boost/pfr/detail/core_name.hpp b/include/boost/pfr/detail/core_name.hpp index e5846c6..10e5983 100644 --- a/include/boost/pfr/detail/core_name.hpp +++ b/include/boost/pfr/detail/core_name.hpp @@ -19,7 +19,7 @@ // // The whole functional of extracting field's names is build on top of those // two functions. -#if BOOST_PFR_ENABLE_GETTING_NAMES +#if BOOST_PFR_ENABLE_GET_NAME_STATIC #include #else #include diff --git a/include/boost/pfr/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index b9271e5..535da42 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -17,19 +17,13 @@ namespace boost { namespace pfr { namespace detail { -// TODO: move it outside -template -constexpr auto make_sequence_tuple(Args... args) noexcept { - return sequence_tuple::tuple{ args... }; -} - template constexpr auto get_name() noexcept { static_assert( sizeof(T) && false, "====================> Boost.PFR: Field's names extracting functionality requires C++20 and compiler that supports __PRETTY_FUNCTION__ or __FUNCSIG__ macro (GCC, Clang or MSVC)." ); - + return nullptr; } @@ -40,7 +34,7 @@ constexpr auto tie_as_names_tuple() noexcept { "====================> Boost.PFR: Field's names extracting functionality requires C++20 and compiler that supports __PRETTY_FUNCTION__ or __FUNCSIG__ macro (GCC, Clang or MSVC)." ); - return detail::make_sequence_tuple(); + return detail::sequence_tuple::make_sequence_tuple(); } }}} // namespace boost::pfr::detail diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 0017882..c08fd47 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -24,12 +24,6 @@ namespace boost { namespace pfr { namespace detail { -// TODO: move it outside -template -constexpr auto make_sequence_tuple(Args... args) noexcept { - return sequence_tuple::tuple{ args... }; -} - consteval std::string_view name_of_field_parse(std::string_view sv, std::size_t size_at_begin, std::size_t size_at_end, @@ -39,7 +33,7 @@ consteval std::string_view name_of_field_parse(std::string_view sv, return sv.substr(sv.rfind(until_runtime_last) + until_runtime_last.size()); } -template +template consteval auto name_of_field_impl() noexcept { #ifdef _MSC_VER constexpr auto sv = __FUNCSIG__; @@ -90,7 +84,7 @@ constexpr const T& clang_workaround(const T& arg) noexcept { // Without passing 'T' into 'name_of_field_impl' different fields from different structures might have the same name! template constexpr auto stored_name_of_field = name_of_field_impl( - detail::tie_as_tuple(fake_object) + detail::tie_as_tuple(fake_object) ))>(); #ifdef __clang__ @@ -99,7 +93,7 @@ constexpr auto stored_name_of_field = name_of_field_impl constexpr auto tie_as_names_tuple_impl(std::index_sequence) noexcept { - return detail::make_sequence_tuple(std::string_view{stored_name_of_field.data()}...); + return detail::sequence_tuple::make_sequence_tuple(std::string_view{stored_name_of_field.data()}...); } template @@ -112,8 +106,8 @@ constexpr std::string_view get_name() noexcept { 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(); + + return stored_name_of_field.data(); } template diff --git a/include/boost/pfr/detail/sequence_tuple.hpp b/include/boost/pfr/detail/sequence_tuple.hpp index f8f28ea..07ba462 100644 --- a/include/boost/pfr/detail/sequence_tuple.hpp +++ b/include/boost/pfr/detail/sequence_tuple.hpp @@ -165,6 +165,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 index 3c09eed..dce7c87 100644 --- a/include/boost/pfr/detail/stdarray.hpp +++ b/include/boost/pfr/detail/stdarray.hpp @@ -24,7 +24,7 @@ constexpr auto make_stdarray(const Types&... t) noexcept { template constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence) noexcept { - return make_stdarray( + return detail::make_stdarray( boost::pfr::detail::sequence_tuple::get(t)... ); } diff --git a/misc/strip_boost_namespace.sh b/misc/strip_boost_namespace.sh index 28cc3a0..cba6c9b 100755 --- a/misc/strip_boost_namespace.sh +++ b/misc/strip_boost_namespace.sh @@ -39,13 +39,19 @@ find ${TARGET_PATH}/doc -type f | xargs sed -i 's|Boost.PFR|PFR|g' sed -i 's|# \[Boost.PFR\](https://boost.org/libs/pfr)|# [PFR](https://apolukhin.github.io/pfr_non_boost/)|g' ${TARGET_PATH}/README.md echo -n "***** Testing: " -if g++-12 -std=c++2a -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=1 -DPFR_ENABLE_GETTING_NAMES=1 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /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++-12 -std=c++2a -DPFR_USE_LOOPHOLE=0 -DPFR_USE_CPP17=1 -DPFR_ENABLE_GETTING_NAMES=0 -I ${TARGET_PATH}/include/ ${TARGET_PATH}/example/motivating_example0.cpp && ./a.out > /dev/null; then +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" + exit 2 +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" diff --git a/test/config/print_config.cpp b/test/config/print_config.cpp index 78e0e1b..cc49fba 100644 --- a/test/config/print_config.cpp +++ b/test/config/print_config.cpp @@ -14,7 +14,7 @@ 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_ENABLE_GETTING_NAMES == " << BOOST_PFR_ENABLE_GETTING_NAMES << '\n' + << "BOOST_PFR_ENABLE_GET_NAME_STATIC == " << BOOST_PFR_ENABLE_GET_NAME_STATIC << '\n' << "BOOST_PFR_ENABLED == " << BOOST_PFR_ENABLED << '\n' << "__cplusplus == " << __cplusplus << '\n' #ifdef __cpp_structured_bindings diff --git a/test/core_name/fields_names.cpp b/test/core_name/fields_names.cpp index d0a07bc..0d0fad2 100644 --- a/test/core_name/fields_names.cpp +++ b/test/core_name/fields_names.cpp @@ -22,9 +22,9 @@ struct nonconstexpr { struct Aggregate { int member1; - nonconstexpr this_is_a_name; + nonconstexpr this_is_a_name; std::reference_wrapper c; - std::string Forth; + std::string Forth; }; struct A { diff --git a/test/core_name/fields_names_constexpr.cpp b/test/core_name/fields_names_constexpr.cpp index 8b3ebef..2288d62 100644 --- a/test/core_name/fields_names_constexpr.cpp +++ b/test/core_name/fields_names_constexpr.cpp @@ -19,9 +19,9 @@ struct nonconstexpr { struct Aggregate { int member1; - nonconstexpr this_is_a_name; + nonconstexpr this_is_a_name; std::reference_wrapper c; - std::string Forth; + std::string Forth; }; static_assert(boost::pfr::get_name<0, Aggregate>() == "member1"); diff --git a/test/core_name/generate_fields_names_big.cpp.py b/test/core_name/generate_fields_names_big.cpp.py index 30f7fc8..fe5759b 100644 --- a/test/core_name/generate_fields_names_big.cpp.py +++ b/test/core_name/generate_fields_names_big.cpp.py @@ -73,7 +73,7 @@ TEST_GET_NAMES_AS_ARRAY_TEMPLATE = """void test_names_as_array_%TEST_ID%() { for (std::size_t i=0;i Date: Tue, 29 Aug 2023 02:03:20 +0300 Subject: [PATCH 13/30] Parsing ala boost type_index --- doc/pfr.qbk | 9 ++ include/boost/pfr/config.hpp | 41 +++++++-- .../boost/pfr/detail/core_name14_disabled.hpp | 4 +- .../boost/pfr/detail/core_name20_static.hpp | 86 ++++++++++++++----- test/config/print_config.cpp | 3 + test/core_name/Jamfile.v2 | 12 ++- ...fields_names_could_not_detect_compiler.cpp | 20 +++++ .../fields_names_misconfigured_compiler.cpp | 21 +++++ test/core_name/print_name.cpp | 33 +++++++ 9 files changed, 196 insertions(+), 33 deletions(-) create mode 100644 test/core_name/compile-fail/fields_names_could_not_detect_compiler.cpp create mode 100644 test/core_name/compile-fail/fields_names_misconfigured_compiler.cpp create mode 100644 test/core_name/print_name.cpp diff --git a/doc/pfr.qbk b/doc/pfr.qbk index d810eac..30b327a 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -508,6 +508,8 @@ By default Boost.PFR [*auto-detects your compiler abilities] and automatically d [[*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_ENABLE_GET_NAME_STATIC*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_ENABLE_GET_NAME_STATIC 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*] [TODO: desc it ]] + [[*BOOST_PFR_CORE_NAME_PARSING*] [TODO: desc it ]] [[*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. ]] ] @@ -528,6 +530,13 @@ The Boost.PFRs reflection has some limitations that depend on a C++ Standard and [endsect] + +[section Limitations of field's names reflection] + +TODO: write the article and make a link on it inside "Limitations and Configuration" + +[endsect] + [section How it works] Short description: diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 8c24982..1fb55c9 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -99,18 +99,45 @@ #endif #ifndef BOOST_PFR_ENABLE_GET_NAME_STATIC -# if (defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911) || (__cplusplus >= 202002L && defined(__clang_major__) && __clang_major__ >= 12) -// Only these 3 compilers have a macro to extract func name -# if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) -# define BOOST_PFR_ENABLE_GET_NAME_STATIC 1 -# else -# define BOOST_PFR_ENABLE_GET_NAME_STATIC 0 -# endif +# if (defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911) \ + || (__cplusplus >= 202002L && defined(__clang_major__) && __clang_major__ >= 12) +# define BOOST_PFR_ENABLE_GET_NAME_STATIC 1 # else # define BOOST_PFR_ENABLE_GET_NAME_STATIC 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 +// TODO: specify in the doc that this is unsupported value +# 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, 16, "->") +# elif defined(__clang__) + // sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1 +# define BOOST_PFR_CORE_NAME_PARSING (64, 2, ".") +# 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, 2, "::") +# else +// TODO: specify in the doc that this is unsupported value +// TODO: .. and even if value is supported, there still no gurantee that it correct! also make a compile-fail test for such case + // 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/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index 535da42..115e838 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -21,7 +21,7 @@ template constexpr auto get_name() noexcept { static_assert( sizeof(T) && false, - "====================> Boost.PFR: Field's names extracting functionality requires C++20 and compiler that supports __PRETTY_FUNCTION__ or __FUNCSIG__ macro (GCC, Clang or MSVC)." + "====================> Boost.PFR: Field's names extracting functionality requires C++20." ); return nullptr; @@ -31,7 +31,7 @@ template constexpr auto tie_as_names_tuple() noexcept { static_assert( sizeof(T) && false, - "====================> Boost.PFR: Field's names extracting functionality requires C++20 and compiler that supports __PRETTY_FUNCTION__ or __FUNCSIG__ macro (GCC, Clang or MSVC)." + "====================> Boost.PFR: Field's names extracting functionality requires C++20." ); return detail::sequence_tuple::make_sequence_tuple(); diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index c08fd47..5a07433 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,33 +25,72 @@ namespace boost { namespace pfr { namespace detail { -consteval std::string_view name_of_field_parse(std::string_view sv, - std::size_t size_at_begin, - std::size_t size_at_end, - std::string_view until_runtime_last) noexcept { - sv.remove_prefix(size_at_begin); - sv.remove_suffix(size_at_end); - return sv.substr(sv.rfind(until_runtime_last) + until_runtime_last.size()); +struct core_name_skip { + std::size_t size_at_begin; + std::size_t size_at_end; + bool more_at_runtime; + std::string_view until_runtime_last; +}; + +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_last) noexcept +{ + return core_name_skip{size_at_begin, size_at_end, more_at_runtime, until_runtime_last}; +} + +consteval core_name_skip make_core_name_skip(std::size_t size_at_begin, + std::size_t size_at_end, + std::string_view until_runtime_last) noexcept +{ + return core_name_skip{size_at_begin, size_at_end, true, until_runtime_last}; +} + +consteval std::string_view apply_core_name_skip(std::string_view sv, + core_name_skip s) noexcept { + sv.remove_prefix((std::min)(s.size_at_begin, sv.size())); + sv.remove_suffix((std::min)(s.size_at_end, sv.size())); + return s.more_at_runtime ? sv.substr((std::min)(sv.rfind(s.until_runtime_last) + s.until_runtime_last.size(), sv.size())) + : sv; + ; +} + +template +consteval void assert_compile_time_legths() noexcept { + static_assert( + Condition, + "PFRs 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, + "PFRs 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." + ); } template consteval auto name_of_field_impl() noexcept { -#ifdef _MSC_VER - constexpr auto sv = __FUNCSIG__; - // sizeof("auto __cdecl boost::pfr::detail::name_of_field_impl<") - 1, sizeof(">(void) noexcept") - 1 - constexpr auto fn = detail::name_of_field_parse(sv, 52, 16, "->"); -#elif __clang__ - constexpr auto sv = __PRETTY_FUNCTION__; - // sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1 - constexpr auto fn = detail::name_of_field_parse(sv, 64, 2, "."); -#else // GCC - constexpr auto sv = __PRETTY_FUNCTION__; - // sizeof("consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = ") - 1, sizeof(")]") - 1 - constexpr auto fn = detail::name_of_field_parse(sv, 79, 2, "::"); -#endif - auto res = std::array{}; - std::ranges::copy(fn, res.begin()); - return res; + constexpr std::string_view sv = 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; + constexpr auto fn = detail::apply_core_name_skip(sv, skip); + auto res = std::array{}; + detail::assert_compile_time_legths(); + std::ranges::copy(fn, res.begin()); + return res; + } } template diff --git a/test/config/print_config.cpp b/test/config/print_config.cpp index cc49fba..2b0dc6a 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 @@ -15,6 +16,8 @@ int main() { << "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_ENABLE_GET_NAME_STATIC == " << BOOST_PFR_ENABLE_GET_NAME_STATIC << '\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 index 238f94f..c081e40 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -43,6 +43,8 @@ project [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_template_args : : [ check-target-builds ../core_name//compiler_supports_cxx20_clang_workaround : : no ] ] ; +local ENABLED_ENGINE = BOOST_PFR_ENABLE_GET_NAME_STATIC=1 ; +local DISABLED_ENGINE = BOOST_PFR_ENABLE_GET_NAME_STATIC=0 ; @@ -54,12 +56,20 @@ actions invoke_python_generator make fields_names_nonascii.cpp : generate_fields_names_nonascii.cpp.py : @invoke_python_generator ; make fields_names_big.cpp : generate_fields_names_big.cpp.py : @invoke_python_generator ; -test-suite pfr_tests +test-suite pfr_name_tests : [ run fields_names.cpp : : : : ] [ run fields_names_constexpr.cpp : : : : ] [ run fields_names_nonascii.cpp : : : msvc:"/utf-8" : ] [ run fields_names_big.cpp : : : msvc:"/bigobj" : ] + [ run print_name.cpp : : : always_show_run_output ] ; +for local source_file in [ glob ./compile-fail/*.cpp ] +{ + local target_name = $(source_file[1]:B) ; + pfr_name_tests += [ compile-fail $(source_file) : $(ENABLED_ENGINE) : $(target_name)_on ] ; + pfr_name_tests += [ compile-fail $(source_file) : $(DISABLED_ENGINE) : $(target_name)_off ] ; +} + 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 0000000..894feb9 --- /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_misconfigured_compiler.cpp b/test/core_name/compile-fail/fields_names_misconfigured_compiler.cpp new file mode 100644 index 0000000..bdbb5ca --- /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/print_name.cpp b/test/core_name/print_name.cpp new file mode 100644 index 0000000..d447cc1 --- /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; +} + From 48b9be5070e705dd28d4245b222a1976218a9456 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 2 Sep 2023 22:42:54 +0300 Subject: [PATCH 14/30] Write docs --- doc/Jamfile.v2 | 1 + doc/pfr.qbk | 102 ++++++++++++++++-- example/get_name.cpp | 43 ++++++++ example/quick_examples.cpp | 16 +++ include/boost/pfr/config.hpp | 11 +- include/boost/pfr/core_name.hpp | 24 +++++ .../boost/pfr/detail/core_name20_static.hpp | 2 + 7 files changed, 184 insertions(+), 15 deletions(-) create mode 100644 example/get_name.cpp diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 1370aa2..ce059b0 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 30b327a..f181cb1 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -183,14 +183,14 @@ Boost.PFR adds the following out-of-the-box functionality for aggregate initiali * 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] @@ -205,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] ] [ @@ -450,12 +453,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_reflection [*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: @@ -477,7 +489,7 @@ struct aggregate : empty { // not a SimpleAggregate ``` The library may work with aggregates that don't satisfy the requirements of `SimpleAggregate`, but the behavior tends to be non-portable. -Boost.PFR's extraction of field name works with a `SimpleAggregate` which variables are able to be declared in any other translation unit. +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. ``` @@ -497,6 +509,8 @@ struct { 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_reflection [*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: @@ -508,8 +522,8 @@ By default Boost.PFR [*auto-detects your compiler abilities] and automatically d [[*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_ENABLE_GET_NAME_STATIC*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_ENABLE_GET_NAME_STATIC 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*] [TODO: desc it ]] - [[*BOOST_PFR_CORE_NAME_PARSING*] [TODO: desc it ]] + [[*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_reflection [*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_reflection [*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. ]] ] @@ -527,13 +541,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's names reflection] +[section Limitations of field names reflection] -TODO: write the article and make a link on it inside "Limitations and Configuration" +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 +`` + PFRs 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. + +[macroref 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 +[macroref BOOST_PFR_CORE_NAME_PARSING] macro. + +Here is a short instruction: + +# get the output of `boost::pfr::get_name<0, A::A>()` +# define [macroref 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 0000000..9275d56 --- /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_ENABLE_GET_NAME_STATIC +//[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_ENABLE_GET_NAME_STATIC + 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 f1ca0f6..d209b41 100644 --- a/example/quick_examples.cpp +++ b/example/quick_examples.cpp @@ -106,6 +106,22 @@ void test_examples() { //] } +#if BOOST_PFR_ENABLE_GET_NAME_STATIC + { +//[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/config.hpp b/include/boost/pfr/config.hpp index 1fb55c9..1f96553 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -115,25 +115,22 @@ || defined(__clang__) # define BOOST_PFR_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ # else -// TODO: specify in the doc that this is unsupported value # 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 +// sizeof("auto __cdecl boost::pfr::detail::name_of_field_impl<") - 1, sizeof(">(void) noexcept") - 1 # define BOOST_PFR_CORE_NAME_PARSING (52, 16, "->") # elif defined(__clang__) - // sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1 +// sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1 # define BOOST_PFR_CORE_NAME_PARSING (64, 2, ".") # elif defined(__GNUC__) - // sizeof("consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = ") - 1, sizeof(")]") - 1 +// sizeof("consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = ") - 1, sizeof(")]") - 1 # define BOOST_PFR_CORE_NAME_PARSING (79, 2, "::") # else -// TODO: specify in the doc that this is unsupported value -// TODO: .. and even if value is supported, there still no gurantee that it correct! also make a compile-fail test for such case - // Deafult parser for other platforms... Just skip nothing! +// Deafult parser for other platforms... Just skip nothing! # define BOOST_PFR_CORE_NAME_PARSING (0, 0, "") # endif #endif diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index d2571ca..c237ebc 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -24,8 +24,24 @@ #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 auto get_name() noexcept { return detail::get_name(); @@ -37,6 +53,14 @@ 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::structure_to_tuple(); +/// assert(get<0>(t) == "i"); +/// \endcode template constexpr auto names_as_array() noexcept { return detail::make_stdarray_from_tietuple( diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 5a07433..4eb1e4f 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -116,6 +116,8 @@ constexpr auto clang_workaround(const T& arg) noexcept { template constexpr const T& clang_workaround(const T& arg) noexcept { + // It's everything OK with this compiler + // so we don't need a workaround here return arg; } From 2876b2e793f8e93da0e5d7d0df660f0e70d98f5a Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 3 Sep 2023 02:12:25 +0300 Subject: [PATCH 15/30] Parser might be explicitly tagged as backward --- include/boost/pfr/config.hpp | 6 +- .../boost/pfr/detail/core_name20_static.hpp | 66 +++++++++++++++---- test/core_name/Jamfile.v2 | 2 + ...elds_names_wrongly_configured_compiler.cpp | 21 ++++++ ...ds_names_correctly_configured_compiler.cpp | 25 +++++++ .../fields_names_internal_parser.cpp | 58 ++++++++++++++++ 6 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 test/core_name/compile-fail/fields_names_wrongly_configured_compiler.cpp create mode 100644 test/core_name/fields_names_correctly_configured_compiler.cpp create mode 100644 test/core_name/fields_names_internal_parser.cpp diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 1f96553..8b388e0 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -122,13 +122,13 @@ #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, 16, "->") +# define BOOST_PFR_CORE_NAME_PARSING (52, 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, 2, ".") +# define BOOST_PFR_CORE_NAME_PARSING (64, 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, 2, "::") +# define BOOST_PFR_CORE_NAME_PARSING (79, 2, backward("::")) # else // Deafult parser for other platforms... Just skip nothing! # define BOOST_PFR_CORE_NAME_PARSING (0, 0, "") diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 4eb1e4f..17fb449 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -29,31 +29,64 @@ struct core_name_skip { std::size_t size_at_begin; std::size_t size_at_end; bool more_at_runtime; - std::string_view until_runtime_last; + 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_last) noexcept + std::string_view until_runtime) noexcept { - return core_name_skip{size_at_begin, size_at_end, more_at_runtime, until_runtime_last}; + 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, - std::string_view until_runtime_last) noexcept + bool more_at_runtime, + backward until_runtime) noexcept { - return core_name_skip{size_at_begin, size_at_end, true, until_runtime_last}; + return core_name_skip{size_at_begin, size_at_end, more_at_runtime, true, until_runtime.value}; } -consteval std::string_view apply_core_name_skip(std::string_view sv, - core_name_skip s) noexcept { - sv.remove_prefix((std::min)(s.size_at_begin, sv.size())); - sv.remove_suffix((std::min)(s.size_at_end, sv.size())); - return s.more_at_runtime ? sv.substr((std::min)(sv.rfind(s.until_runtime_last) + s.until_runtime_last.size(), sv.size())) - : sv; - ; +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 @@ -85,7 +118,14 @@ consteval auto name_of_field_impl() noexcept { return detail::make_stdarray(0); } else { constexpr auto skip = detail::make_core_name_skip BOOST_PFR_CORE_NAME_PARSING; - constexpr auto fn = detail::apply_core_name_skip(sv, skip); + 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(); std::ranges::copy(fn, res.begin()); diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index c081e40..5701317 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -62,6 +62,8 @@ test-suite pfr_name_tests [ run fields_names_constexpr.cpp : : : : ] [ run fields_names_nonascii.cpp : : : msvc:"/utf-8" : ] [ run fields_names_big.cpp : : : msvc:"/bigobj" : ] + [ run fields_names_correctly_configured_compiler.cpp : : : : ] + [ run fields_names_internal_parser.cpp : : : : ] [ run print_name.cpp : : : always_show_run_output ] ; 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 0000000..34fbea9 --- /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/fields_names_correctly_configured_compiler.cpp b/test/core_name/fields_names_correctly_configured_compiler.cpp new file mode 100644 index 0000000..3ba542c --- /dev/null +++ b/test/core_name/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/fields_names_internal_parser.cpp b/test/core_name/fields_names_internal_parser.cpp new file mode 100644 index 0000000..bf98287 --- /dev/null +++ b/test/core_name/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(); +} + From 2674cf4fd4be3ae57d517df0f55cd7c526d1a556 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 3 Sep 2023 02:53:57 +0300 Subject: [PATCH 16/30] Fix CI --- example/get_name.cpp | 4 +-- example/quick_examples.cpp | 3 +- .../boost/pfr/detail/core_name20_static.hpp | 31 +++++++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/example/get_name.cpp b/example/get_name.cpp index 9275d56..0b5bf2b 100644 --- a/example/get_name.cpp +++ b/example/get_name.cpp @@ -10,7 +10,7 @@ #include -#if BOOST_PFR_ENABLE_GET_NAME_STATIC +#if BOOST_PFR_ENABLE_GET_NAME_STATIC && 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. @@ -34,7 +34,7 @@ constexpr auto r2 = boost::pfr::get_name<1, foo>(); // reading name of field wit #endif int main() { -#if BOOST_PFR_ENABLE_GET_NAME_STATIC +#if BOOST_PFR_ENABLE_GET_NAME_STATIC && BOOST_PFR_USE_CPP17 if (r1 != "some_integer") return 1; if (r2 != "c") return 2; #endif diff --git a/example/quick_examples.cpp b/example/quick_examples.cpp index d209b41..8166641 100644 --- a/example/quick_examples.cpp +++ b/example/quick_examples.cpp @@ -106,7 +106,8 @@ void test_examples() { //] } -#if BOOST_PFR_ENABLE_GET_NAME_STATIC + // Disabled from testing since it's unportable +#if 0 { //[pfr_quick_examples_get_name // Get name of field by index diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 17fb449..9d0dc37 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -110,9 +110,17 @@ consteval void failed_to_get_function_name() noexcept { ); } +// 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 = BOOST_PFR_FUNCTION_SIGNATURE; + 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); @@ -140,32 +148,37 @@ extern const T fake_object; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundefined-var-template" +// clang 16 doesn'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_workaround_t { +struct clang_wrapper_t { T v; }; template -clang_workaround_t(T) -> clang_workaround_t; +clang_wrapper_t(T) -> clang_wrapper_t; template -constexpr auto clang_workaround(const T& arg) noexcept { - return clang_workaround_t{arg}; +constexpr auto make_clang_wrapper(const T& arg) noexcept { + return clang_wrapper_t{arg}; } #else template -constexpr const T& clang_workaround(const T& arg) noexcept { - // It's everything OK with this compiler - // so we don't need a workaround here +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 = name_of_field_impl( +constexpr auto stored_name_of_field = name_of_field_impl( detail::tie_as_tuple(fake_object) ))>(); From d8a10e2abd42f37a6d109def5c80bd11ca9223a5 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Fri, 8 Sep 2023 00:46:55 +0300 Subject: [PATCH 17/30] Fix strip_boost_namespace.sh --- include/boost/pfr/config.hpp | 6 +++--- misc/strip_boost_namespace.sh | 36 ++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 8b388e0..b9ab97f 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -122,13 +122,13 @@ #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, 16, backward("->")) +# 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, 2, backward(".")) +# 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, 2, backward("::")) +# 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, "") diff --git a/misc/strip_boost_namespace.sh b/misc/strip_boost_namespace.sh index cba6c9b..36cc2b7 100755 --- a/misc/strip_boost_namespace.sh +++ b/misc/strip_boost_namespace.sh @@ -36,6 +36,8 @@ find ${TARGET_PATH} -type f | xargs sed -i 's|boost/pfr|pfr|g' find ${TARGET_PATH}/doc -type f | xargs sed -i 's|boost.pfr.|pfr.|g' find ${TARGET_PATH}/doc -type f | xargs sed -i 's|Boost.PFR|PFR|g' +sed -i 's/[0-9]* \/\*\([0-9]*\) for non boost\*\/\+/\1/g' ${TARGET_PATH}/include/pfr/config.hpp + sed -i 's|# \[Boost.PFR\](https://boost.org/libs/pfr)|# [PFR](https://apolukhin.github.io/pfr_non_boost/)|g' ${TARGET_PATH}/README.md echo -n "***** Testing: " @@ -49,28 +51,36 @@ if g++-12 -std=c++2a -DPFR_ENABLE_GET_NAME_STATIC=0 -I ${TARGET_PATH}/include/ $ echo -n "OK" else echo -n "FAIL" - exit 2 -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 2 -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 3 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 +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" From ec0ea4a338e924d6968fd0c7a6d15dab81bd38e1 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 9 Sep 2023 22:07:12 +0300 Subject: [PATCH 18/30] Fix docs --- doc/pfr.qbk | 15 ++++++++------- include/boost/pfr/core_name.hpp | 20 ++++++++++++++++---- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/doc/pfr.qbk b/doc/pfr.qbk index f181cb1..7341a5b 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -274,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?] @@ -458,7 +459,7 @@ error: static_assert failed "====================> Boost.PFR: For safety reasons [pfr_example_get_name] -See [link boost_pfr.limitations_of_field_names_reflection [*Limitations of field names reflection]] and [link boost_pfr.limitations_and_configuration [*Limitations and Configuration]]. +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] @@ -509,7 +510,7 @@ struct { 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_reflection [*Limitations of field names reflection]] for details. +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,8 +523,8 @@ By default Boost.PFR [*auto-detects your compiler abilities] and automatically d [[*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_ENABLE_GET_NAME_STATIC*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_ENABLE_GET_NAME_STATIC 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_reflection [*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_reflection [*here]]. ]] + [[*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. ]] ] @@ -563,7 +564,7 @@ If you get the following error during compilation `` then you are using a compiler that was not tested with this library. -[macroref BOOST_PFR_FUNCTION_SIGNATURE] must be defined to a compiler specific macro, that outputs the *whole* +BOOST_PFR_FUNCTION_SIGNATURE must be defined to a compiler specific macro, that outputs the *whole* function signature including non-type template parameters. @@ -577,12 +578,12 @@ 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 -[macroref BOOST_PFR_CORE_NAME_PARSING] macro. +BOOST_PFR_CORE_NAME_PARSING macro. Here is a short instruction: # get the output of `boost::pfr::get_name<0, A::A>()` -# define [macroref BOOST_PFR_CORE_NAME_PARSING] to +# 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 diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index c237ebc..d6402f5 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -43,7 +43,13 @@ namespace boost { namespace pfr { /// assert(boost::pfr::get_name<1, my_struct>() == "s"); /// \endcode template -constexpr auto get_name() noexcept { +constexpr +#ifdef BOOST_PFR_DOXYGEN_INVOKED +std::string_view +#else +auto +#endif +get_name() noexcept { return detail::get_name(); } @@ -58,11 +64,17 @@ constexpr auto get_name() noexcept { /// \b Example: /// \code /// struct my_struct { int i, short s; }; -/// std::array t = boost::pfr::structure_to_tuple(); -/// assert(get<0>(t) == "i"); +/// std::array t = boost::pfr::names_as_array(); +/// assert(t[0] == "i"); /// \endcode template -constexpr auto names_as_array() noexcept { +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 >() From 245ac56645497cd3983b76ffc842b967f84d43c7 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 9 Sep 2023 22:41:10 +0300 Subject: [PATCH 19/30] Rename C++20 features detectors --- test/core_name/Jamfile.v2 | 10 +++++----- ..._address_of_non_static_member_tplarg_detection.cpp} | 0 ...etection.cpp => cxx20_nontype_tplarg_detection.cpp} | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename test/core_name/{cxx20_nontype_template_args_detection.cpp => cxx20_address_of_non_static_member_tplarg_detection.cpp} (100%) rename test/core_name/{cxx20_clang_workaround_detection.cpp => cxx20_nontype_tplarg_detection.cpp} (100%) diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index 5701317..b659ca7 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -28,11 +28,11 @@ rule mp-run-simple ( sources + : args * : input-files * : requirements * : targe alias $(target-name) : $(target-name).output ; } -mp-run-simple cxx20_nontype_template_args_detection.cpp : : : : compiler_supports_cxx20_nontype_template_args ; -explicit compiler_supports_cxx20_nontype_template_args ; +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_clang_workaround_detection.cpp : : : : compiler_supports_cxx20_clang_workaround ; -explicit compiler_supports_cxx20_clang_workaround ; +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 @@ -40,7 +40,7 @@ project : source-location . : requirements BOOST_PFR_DETAIL_STRICT_RVALUE_TESTING=1 - [ check-target-builds ../core_name//compiler_supports_cxx20_nontype_template_args : : [ check-target-builds ../core_name//compiler_supports_cxx20_clang_workaround : : no ] ] + [ 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 ] ] ; local ENABLED_ENGINE = BOOST_PFR_ENABLE_GET_NAME_STATIC=1 ; diff --git a/test/core_name/cxx20_nontype_template_args_detection.cpp b/test/core_name/cxx20_address_of_non_static_member_tplarg_detection.cpp similarity index 100% rename from test/core_name/cxx20_nontype_template_args_detection.cpp rename to test/core_name/cxx20_address_of_non_static_member_tplarg_detection.cpp diff --git a/test/core_name/cxx20_clang_workaround_detection.cpp b/test/core_name/cxx20_nontype_tplarg_detection.cpp similarity index 100% rename from test/core_name/cxx20_clang_workaround_detection.cpp rename to test/core_name/cxx20_nontype_tplarg_detection.cpp From 7f5895b3a300cbf9190b5fa2606a504dbe79f61f Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 10 Sep 2023 01:22:14 +0300 Subject: [PATCH 20/30] review --- doc/pfr.qbk | 24 +++++++-------- example/get_name.cpp | 4 +-- include/boost/pfr/config.hpp | 14 +++++---- include/boost/pfr/core_name.hpp | 15 ++++++++-- include/boost/pfr/detail/core_name.hpp | 2 +- .../boost/pfr/detail/core_name20_static.hpp | 29 +++++++++---------- include/boost/pfr/detail/fake_object.hpp | 25 ++++++++++++++++ include/boost/pfr/detail/sequence_tuple.hpp | 3 ++ include/boost/pfr/detail/stdarray.hpp | 7 ++++- test/config/print_config.cpp | 2 +- test/core_name/Jamfile.v2 | 28 +++++++++--------- .../fields_names_disabled_via_macro_00.cpp | 20 +++++++++++++ .../fields_names_disabled_via_macro_01.cpp | 20 +++++++++++++ .../fields_names_get_name_on_union.cpp | 22 ++++++++++++++ .../fields_names_names_as_array_on_union.cpp | 22 ++++++++++++++ test/core_name/{ => run}/fields_names.cpp | 8 +++++ .../{ => run}/fields_names_constexpr.cpp | 0 ...ds_names_correctly_configured_compiler.cpp | 0 .../fields_names_internal_parser.cpp | 2 +- .../generate_fields_names_big.cpp.py | 0 .../generate_fields_names_nonascii.cpp.py | 0 21 files changed, 192 insertions(+), 55 deletions(-) create mode 100644 include/boost/pfr/detail/fake_object.hpp create mode 100644 test/core_name/compile-fail/fields_names_disabled_via_macro_00.cpp create mode 100644 test/core_name/compile-fail/fields_names_disabled_via_macro_01.cpp create mode 100644 test/core_name/compile-fail/fields_names_get_name_on_union.cpp create mode 100644 test/core_name/compile-fail/fields_names_names_as_array_on_union.cpp rename test/core_name/{ => run}/fields_names.cpp (89%) rename test/core_name/{ => run}/fields_names_constexpr.cpp (100%) rename test/core_name/{ => run}/fields_names_correctly_configured_compiler.cpp (100%) rename test/core_name/{ => run}/fields_names_internal_parser.cpp (97%) rename test/core_name/{ => run}/generate_fields_names_big.cpp.py (100%) rename test/core_name/{ => run}/generate_fields_names_nonascii.cpp.py (100%) diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 7341a5b..f9e8e72 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -522,7 +522,7 @@ 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_ENABLE_GET_NAME_STATIC*] [On platforms where field's names extracting is not supported, the 'boost/pfr/config.hpp' header defines the BOOST_PFR_ENABLE_GET_NAME_STATIC macro equal to 0. Defining this macro as 0 before including the header disables the ability to get a field's name. ]] + [[*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. ]] @@ -556,13 +556,13 @@ Boost.PFRs extraction of field name has been tested and successfully work on man [section Define the BOOST_PFR_FUNCTION_SIGNATURE macro] If you get the following error during compilation -`` - PFRs 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. +``` +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. @@ -574,8 +574,8 @@ function signature including non-type template parameters. 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` +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. @@ -583,9 +583,9 @@ 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 +# 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_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 diff --git a/example/get_name.cpp b/example/get_name.cpp index 0b5bf2b..5f5c7eb 100644 --- a/example/get_name.cpp +++ b/example/get_name.cpp @@ -10,7 +10,7 @@ #include -#if BOOST_PFR_ENABLE_GET_NAME_STATIC && BOOST_PFR_USE_CPP17 +#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. @@ -34,7 +34,7 @@ constexpr auto r2 = boost::pfr::get_name<1, foo>(); // reading name of field wit #endif int main() { -#if BOOST_PFR_ENABLE_GET_NAME_STATIC && BOOST_PFR_USE_CPP17 +#if BOOST_PFR_CORE_NAME_ENABLED && BOOST_PFR_USE_CPP17 if (r1 != "some_integer") return 1; if (r2 != "c") return 2; #endif diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index b9ab97f..faa6689 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -98,12 +98,16 @@ # endif #endif -#ifndef BOOST_PFR_ENABLE_GET_NAME_STATIC -# if (defined(__cpp_nontype_template_args) && __cpp_nontype_template_args >= 201911) \ - || (__cplusplus >= 202002L && defined(__clang_major__) && __clang_major__ >= 12) -# define BOOST_PFR_ENABLE_GET_NAME_STATIC 1 +#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_ENABLE_GET_NAME_STATIC 0 +# define BOOST_PFR_CORE_NAME_ENABLED 0 # endif #endif diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index d6402f5..1103af9 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -20,7 +20,8 @@ #include #include -#include +#include // for std::size_t +#include // for std::enable_if_t #include @@ -74,13 +75,23 @@ std::array> #else auto #endif -names_as_array() noexcept { +names_as_array( +#ifndef BOOST_PFR_DOXYGEN_INVOKED + std::enable_if_t())::empty()>* = nullptr +#endif + ) noexcept { return detail::make_stdarray_from_tietuple( detail::tie_as_names_tuple(), detail::make_index_sequence< tuple_size_v >() ); } +#ifndef BOOST_PFR_DOXYGEN_INVOKED +template +constexpr auto names_as_array(std::enable_if_t())::empty()>* = nullptr) noexcept { + return detail::make_empty_stdarray(); +} +#endif }} // namespace boost::pfr diff --git a/include/boost/pfr/detail/core_name.hpp b/include/boost/pfr/detail/core_name.hpp index 10e5983..f266ef2 100644 --- a/include/boost/pfr/detail/core_name.hpp +++ b/include/boost/pfr/detail/core_name.hpp @@ -19,7 +19,7 @@ // // The whole functional of extracting field's names is build on top of those // two functions. -#if BOOST_PFR_ENABLE_GET_NAME_STATIC +#if BOOST_PFR_CORE_NAME_ENABLED #include #else #include diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 9d0dc37..78e1abf 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -18,10 +18,12 @@ #include #include #include +#include #include #include #include #include // for std::ranges::copy +#include // for std::addressof namespace boost { namespace pfr { namespace detail { @@ -62,7 +64,7 @@ struct backward { explicit consteval backward(std::string_view value) noexcept : value(value) {} - + std::string_view value; }; @@ -93,7 +95,7 @@ template consteval void assert_compile_time_legths() noexcept { static_assert( Condition, - "PFRs extraction of field name is misconfigured for your compiler. " + "====================> 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." ); @@ -103,7 +105,7 @@ template consteval void failed_to_get_function_name() noexcept { static_assert( sizeof(T) && false, - "PFRs extraction of field name could not detect your compiler. " + "====================> 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." @@ -118,7 +120,7 @@ consteval std::string_view clang_workaround(std::string_view value) noexcept return value; } -template +template consteval auto name_of_field_impl() noexcept { constexpr std::string_view sv = detail::clang_workaround(BOOST_PFR_FUNCTION_SIGNATURE); if constexpr (sv.empty()) { @@ -141,32 +143,29 @@ consteval auto name_of_field_impl() noexcept { } } -template -extern const T fake_object; - #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundefined-var-template" -// clang 16 doesn't support address of non-static member as template parameter +// 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 +template struct clang_wrapper_t { T v; }; -template +template clang_wrapper_t(T) -> clang_wrapper_t; -template +template constexpr auto make_clang_wrapper(const T& arg) noexcept { return clang_wrapper_t{arg}; } #else -template +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 @@ -178,9 +177,9 @@ constexpr const T& make_clang_wrapper(const T& arg) noexcept { // 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 = name_of_field_impl( +constexpr auto stored_name_of_field = detail::name_of_field_impl( detail::tie_as_tuple(fake_object) -))>(); +)))>(); #ifdef __clang__ #pragma clang diagnostic pop @@ -216,7 +215,7 @@ constexpr auto tie_as_names_tuple() noexcept { "====================> Boost.PFR: Extraction of field's names is allowed only when the BOOST_PFR_USE_CPP17 macro enabled." ); - return tie_as_names_tuple_impl(detail::make_index_sequence()>{}); + return detail::tie_as_names_tuple_impl(detail::make_index_sequence()>{}); } }}} // namespace boost::pfr::detail diff --git a/include/boost/pfr/detail/fake_object.hpp b/include/boost/pfr/detail/fake_object.hpp new file mode 100644 index 0000000..2e37e97 --- /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 07ba462..fcbb837 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; } }; diff --git a/include/boost/pfr/detail/stdarray.hpp b/include/boost/pfr/detail/stdarray.hpp index dce7c87..d6de1ea 100644 --- a/include/boost/pfr/detail/stdarray.hpp +++ b/include/boost/pfr/detail/stdarray.hpp @@ -10,8 +10,9 @@ #include #include // metaprogramming stuff -#include +#include #include // for std::common_type_t +#include #include @@ -29,6 +30,10 @@ constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence ); } +constexpr auto make_empty_stdarray() noexcept { + return std::array{}; +} + }}} // namespace boost::pfr::detail #endif // BOOST_PFR_DETAIL_STDARRAY_HPP diff --git a/test/config/print_config.cpp b/test/config/print_config.cpp index 2b0dc6a..b9c0e77 100644 --- a/test/config/print_config.cpp +++ b/test/config/print_config.cpp @@ -15,7 +15,7 @@ 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_ENABLE_GET_NAME_STATIC == " << BOOST_PFR_ENABLE_GET_NAME_STATIC << '\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' diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index b659ca7..5d29178 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -43,9 +43,6 @@ project [ 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 ] ] ; -local ENABLED_ENGINE = BOOST_PFR_ENABLE_GET_NAME_STATIC=1 ; -local DISABLED_ENGINE = BOOST_PFR_ENABLE_GET_NAME_STATIC=0 ; - actions invoke_python_generator @@ -53,25 +50,26 @@ actions invoke_python_generator python $(>) > $(<) } -make fields_names_nonascii.cpp : generate_fields_names_nonascii.cpp.py : @invoke_python_generator ; -make fields_names_big.cpp : generate_fields_names_big.cpp.py : @invoke_python_generator ; - test-suite pfr_name_tests : - [ run fields_names.cpp : : : : ] - [ run fields_names_constexpr.cpp : : : : ] - [ run fields_names_nonascii.cpp : : : msvc:"/utf-8" : ] - [ run fields_names_big.cpp : : : msvc:"/bigobj" : ] - [ run fields_names_correctly_configured_compiler.cpp : : : : ] - [ run fields_names_internal_parser.cpp : : : : ] [ run print_name.cpp : : : always_show_run_output ] ; +for local source_file in [ glob ./run/*.cpp ] +{ + pfr_name_tests += [ run $(source_file) : : : : ] ; +} + +for local source_file in [ glob ./run/*.py ] +{ + local cpp_source_file = $(source_file[1]:B) ; + make $(cpp_source_file) : $(source_file) : @invoke_python_generator ; + pfr_name_tests += [ run $(cpp_source_file) : : : msvc:"/utf-8" msvc:"/bigobj" : ] ; +} + for local source_file in [ glob ./compile-fail/*.cpp ] { - local target_name = $(source_file[1]:B) ; - pfr_name_tests += [ compile-fail $(source_file) : $(ENABLED_ENGINE) : $(target_name)_on ] ; - pfr_name_tests += [ compile-fail $(source_file) : $(DISABLED_ENGINE) : $(target_name)_off ] ; + pfr_name_tests += [ compile-fail $(source_file) : : ] ; } 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 0000000..7cda619 --- /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 0000000..392e397 --- /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 0000000..e4f9e63 --- /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_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 0000000..d61dd0d --- /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/fields_names.cpp b/test/core_name/run/fields_names.cpp similarity index 89% rename from test/core_name/fields_names.cpp rename to test/core_name/run/fields_names.cpp index 0d0fad2..ca7acbd 100644 --- a/test/core_name/fields_names.cpp +++ b/test/core_name/run/fields_names.cpp @@ -32,6 +32,8 @@ struct A { 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"); @@ -64,6 +66,11 @@ void test_names_as_array() { } } +void test_names_as_array_for_empty() { + const auto value = boost::pfr::names_as_array(); + BOOST_TEST_EQ(value.size(), 0); + BOOST_TEST_EQ(value.empty(), true); +} } // namespace testing @@ -71,6 +78,7 @@ 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/fields_names_constexpr.cpp b/test/core_name/run/fields_names_constexpr.cpp similarity index 100% rename from test/core_name/fields_names_constexpr.cpp rename to test/core_name/run/fields_names_constexpr.cpp diff --git a/test/core_name/fields_names_correctly_configured_compiler.cpp b/test/core_name/run/fields_names_correctly_configured_compiler.cpp similarity index 100% rename from test/core_name/fields_names_correctly_configured_compiler.cpp rename to test/core_name/run/fields_names_correctly_configured_compiler.cpp diff --git a/test/core_name/fields_names_internal_parser.cpp b/test/core_name/run/fields_names_internal_parser.cpp similarity index 97% rename from test/core_name/fields_names_internal_parser.cpp rename to test/core_name/run/fields_names_internal_parser.cpp index bf98287..b4d5802 100644 --- a/test/core_name/fields_names_internal_parser.cpp +++ b/test/core_name/run/fields_names_internal_parser.cpp @@ -16,7 +16,7 @@ namespace testing { -constexpr std::string_view fake_func_name = " ******************** [fake_text1->fake_text2->fake_text3] **********"; +constexpr std::string_view fake_func_name = " ******************** [fake_text1->fake_text2->fake_text3] **********"; void test_general() { diff --git a/test/core_name/generate_fields_names_big.cpp.py b/test/core_name/run/generate_fields_names_big.cpp.py similarity index 100% rename from test/core_name/generate_fields_names_big.cpp.py rename to test/core_name/run/generate_fields_names_big.cpp.py diff --git a/test/core_name/generate_fields_names_nonascii.cpp.py b/test/core_name/run/generate_fields_names_nonascii.cpp.py similarity index 100% rename from test/core_name/generate_fields_names_nonascii.cpp.py rename to test/core_name/run/generate_fields_names_nonascii.cpp.py From 7738b091328c90b50343c9165e560df72814e8d9 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 10 Sep 2023 05:19:20 +0300 Subject: [PATCH 21/30] Fix for old MSVC compiler --- include/boost/pfr/core_name.hpp | 17 +++-------------- include/boost/pfr/detail/stdarray.hpp | 5 +++-- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index 1103af9..e64613d 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -21,7 +21,6 @@ #include #include // for std::size_t -#include // for std::enable_if_t #include @@ -75,24 +74,14 @@ std::array> #else auto #endif -names_as_array( -#ifndef BOOST_PFR_DOXYGEN_INVOKED - std::enable_if_t())::empty()>* = nullptr -#endif - ) noexcept { +names_as_array() noexcept { return detail::make_stdarray_from_tietuple( detail::tie_as_names_tuple(), - detail::make_index_sequence< tuple_size_v >() + detail::make_index_sequence< tuple_size_v >(), + 1L ); } -#ifndef BOOST_PFR_DOXYGEN_INVOKED -template -constexpr auto names_as_array(std::enable_if_t())::empty()>* = nullptr) noexcept { - return detail::make_empty_stdarray(); -} -#endif - }} // namespace boost::pfr #endif // BOOST_PFR_CORE_NAME_HPP diff --git a/include/boost/pfr/detail/stdarray.hpp b/include/boost/pfr/detail/stdarray.hpp index d6de1ea..afaddd2 100644 --- a/include/boost/pfr/detail/stdarray.hpp +++ b/include/boost/pfr/detail/stdarray.hpp @@ -24,13 +24,14 @@ constexpr auto make_stdarray(const Types&... t) noexcept { } template -constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence) noexcept { +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)... ); } -constexpr auto make_empty_stdarray() noexcept { +template +constexpr auto make_stdarray_from_tietuple(const T& t, std::index_sequence<>, long) noexcept { return std::array{}; } From 95a2044e8b3166dac6d227acf998d52f80a68407 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 17 Sep 2023 13:41:43 +0300 Subject: [PATCH 22/30] relax standard library requirements --- include/boost/pfr/detail/core_name20_static.hpp | 9 +++++++-- test/core_name/run/fields_names_constexpr.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 78e1abf..7671975 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -22,7 +22,6 @@ #include #include #include -#include // for std::ranges::copy #include // for std::addressof namespace boost { namespace pfr { namespace detail { @@ -138,7 +137,13 @@ consteval auto name_of_field_impl() noexcept { constexpr auto fn = skip.apply(sv); auto res = std::array{}; detail::assert_compile_time_legths(); - std::ranges::copy(fn, res.begin()); + + auto* out = res.begin(); + for (auto x: fn) { + *out = x; + ++out; + } + return res; } } diff --git a/test/core_name/run/fields_names_constexpr.cpp b/test/core_name/run/fields_names_constexpr.cpp index 2288d62..d539971 100644 --- a/test/core_name/run/fields_names_constexpr.cpp +++ b/test/core_name/run/fields_names_constexpr.cpp @@ -29,12 +29,12 @@ 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"); -static_assert(boost::pfr::names_as_array() == std::array{ - "member1", - "this_is_a_name", - "c", - "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() {} From f23c003c5180565c76d28e88b425bb01c10b81eb Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 17 Sep 2023 13:55:47 +0300 Subject: [PATCH 23/30] Do not require Python to run tests --- .../generate_fields_names_big.cpp.py | 10 +- test/core_name/Jamfile.v2 | 17 +- test/core_name/run/fields_names_big.cpp | 170 ++++++++++++++++++ test/core_name/run/fields_names_nonascii.cpp | 40 +++++ .../run/generate_fields_names_nonascii.cpp.py | 55 ------ 5 files changed, 219 insertions(+), 73 deletions(-) rename {test/core_name/run => misc}/generate_fields_names_big.cpp.py (95%) create mode 100644 test/core_name/run/fields_names_big.cpp create mode 100644 test/core_name/run/fields_names_nonascii.cpp delete mode 100644 test/core_name/run/generate_fields_names_nonascii.cpp.py diff --git a/test/core_name/run/generate_fields_names_big.cpp.py b/misc/generate_fields_names_big.cpp.py similarity index 95% rename from test/core_name/run/generate_fields_names_big.cpp.py rename to misc/generate_fields_names_big.cpp.py index fe5759b..deb824b 100644 --- a/test/core_name/run/generate_fields_names_big.cpp.py +++ b/misc/generate_fields_names_big.cpp.py @@ -18,8 +18,14 @@ import random STRUCT_COUNT = 50 MAX_FIELD_COUNT = 50 -MAIN_TEMPLATE = """#include -#include +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 diff --git a/test/core_name/Jamfile.v2 b/test/core_name/Jamfile.v2 index 5d29178..13b1e4c 100644 --- a/test/core_name/Jamfile.v2 +++ b/test/core_name/Jamfile.v2 @@ -8,7 +8,6 @@ # The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 # -import python ; import testing ; import ../../config/checks/config : requires ; @@ -43,13 +42,6 @@ project [ 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 ] ] ; - - -actions invoke_python_generator -{ - python $(>) > $(<) -} - test-suite pfr_name_tests : [ run print_name.cpp : : : always_show_run_output ] @@ -57,14 +49,7 @@ test-suite pfr_name_tests for local source_file in [ glob ./run/*.cpp ] { - pfr_name_tests += [ run $(source_file) : : : : ] ; -} - -for local source_file in [ glob ./run/*.py ] -{ - local cpp_source_file = $(source_file[1]:B) ; - make $(cpp_source_file) : $(source_file) : @invoke_python_generator ; - pfr_name_tests += [ run $(cpp_source_file) : : : msvc:"/utf-8" msvc:"/bigobj" : ] ; + pfr_name_tests += [ run $(source_file) : : : msvc:"/utf-8" msvc:"/bigobj" : ] ; } for local source_file in [ glob ./compile-fail/*.cpp ] 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 0000000..da1f2f4 --- /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 + +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 - -#include - -namespace testing { - -struct Aggregate { - int _%ARG%; -}; - -void test_get_name() { - BOOST_TEST_EQ( ((boost::pfr::get_name<0, Aggregate>())), "_%ARG%"); -} - -void test_names_as_array() { - const auto expected = std::array{ - "_%ARG%" - }; - const auto value = boost::pfr::names_as_array(); - BOOST_TEST_EQ(expected.size(), value.size()); - for (std::size_t i=0;i Date: Sun, 17 Sep 2023 14:14:26 +0300 Subject: [PATCH 24/30] Fix compilation on MSVC --- include/boost/pfr/detail/core_name20_static.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 7671975..0835110 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -138,7 +138,7 @@ consteval auto name_of_field_impl() noexcept { auto res = std::array{}; detail::assert_compile_time_legths(); - auto* out = res.begin(); + auto* out = res.data(); for (auto x: fn) { *out = x; ++out; From df1249f6b878f34c4291e9f9d2610f01f0b24566 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 17 Sep 2023 15:15:40 +0300 Subject: [PATCH 25/30] Suppress non-ASCII warning from boost-inspect --- test/core_name/run/fields_names_nonascii.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/core_name/run/fields_names_nonascii.cpp b/test/core_name/run/fields_names_nonascii.cpp index 00e9113..56c25fc 100644 --- a/test/core_name/run/fields_names_nonascii.cpp +++ b/test/core_name/run/fields_names_nonascii.cpp @@ -3,6 +3,10 @@ // 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 From ba25324b12891edb20102928d8e45fb2a0cf08ae Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 17 Sep 2023 18:06:35 +0300 Subject: [PATCH 26/30] Avoid stored_name_of_field duplication in different translation units --- include/boost/pfr/detail/core_name20_static.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 0835110..086fe89 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -179,12 +179,16 @@ constexpr const T& make_clang_wrapper(const T& arg) noexcept { #endif +// 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! // 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) -)))>(); +inline constexpr auto stored_name_of_field = detail::name_of_field_impl( + detail::tie_as_tuple(fake_object) + ))) +>(); #ifdef __clang__ #pragma clang diagnostic pop From e48f84dcb18acc04ae53e476e15d44a735a52123 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 18 Sep 2023 13:57:58 +0300 Subject: [PATCH 27/30] 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(); From 8a13352854aa7f14695064b21cd71c51a575f1bd Mon Sep 17 00:00:00 2001 From: Denis Mikhailov Date: Tue, 19 Sep 2023 11:46:52 +0300 Subject: [PATCH 28/30] Fix typos (#139) --- doc/pfr.qbk | 4 ++-- example/get_name.cpp | 8 ++++---- include/boost/pfr/core_name.hpp | 4 ++-- misc/strip_boost_namespace.sh | 2 -- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/pfr.qbk b/doc/pfr.qbk index d420efd..131b30d 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -541,13 +541,13 @@ To do that: * `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 +# If it does not return `user_defined_field`, 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 `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 [*and] the initial output if `test/core_name/print_name.cpp`. +parameters provided to `BOOST_PFR_CORE_NAME_PARSING` macro [*and] the initial output of `test/core_name/print_name.cpp`. [endsect] diff --git a/example/get_name.cpp b/example/get_name.cpp index b4c1b2e..0a62c13 100644 --- a/example/get_name.cpp +++ b/example/get_name.cpp @@ -28,15 +28,15 @@ struct foo { // defining structure /*` We can access field's names of that structure by index: */ -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" +constexpr std::string_view n1 = boost::pfr::get_name<0, foo>(); // returns "some_integer" +constexpr std::string_view n2 = boost::pfr::get_name<1, foo>(); // returns "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; + if (n1 != "some_integer") return 1; + if (n2 != "c") return 2; #endif return 0; diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index e64613d..8b0d012 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -64,8 +64,8 @@ get_name() noexcept { /// \b Example: /// \code /// struct my_struct { int i, short s; }; -/// std::array t = boost::pfr::names_as_array(); -/// assert(t[0] == "i"); +/// std::array a = boost::pfr::names_as_array(); +/// assert(a[0] == "i"); /// \endcode template constexpr diff --git a/misc/strip_boost_namespace.sh b/misc/strip_boost_namespace.sh index 36cc2b7..09fbacd 100755 --- a/misc/strip_boost_namespace.sh +++ b/misc/strip_boost_namespace.sh @@ -36,8 +36,6 @@ find ${TARGET_PATH} -type f | xargs sed -i 's|boost/pfr|pfr|g' find ${TARGET_PATH}/doc -type f | xargs sed -i 's|boost.pfr.|pfr.|g' find ${TARGET_PATH}/doc -type f | xargs sed -i 's|Boost.PFR|PFR|g' -sed -i 's/[0-9]* \/\*\([0-9]*\) for non boost\*\/\+/\1/g' ${TARGET_PATH}/include/pfr/config.hpp - sed -i 's|# \[Boost.PFR\](https://boost.org/libs/pfr)|# [PFR](https://apolukhin.github.io/pfr_non_boost/)|g' ${TARGET_PATH}/README.md echo -n "***** Testing: " From b1432d5fa9cc4649ce7615653a256064fb735129 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 23 Sep 2023 01:52:03 +0300 Subject: [PATCH 29/30] Fix for printing BOOST_PFR_FUNCTION_SIGNATURE --- test/config/print_config.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/config/print_config.cpp b/test/config/print_config.cpp index b9c0e77..e116b9d 100644 --- a/test/config/print_config.cpp +++ b/test/config/print_config.cpp @@ -3,7 +3,10 @@ // 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) -#include // inclusion of an another PFR header may fail when BOOST_PFR_ENABLED=0 +#include +#if BOOST_PFR_ENABLED +#include +#endif #include #include From 11133a4f63ef040a0b77bdb2b3e5da296c67a5a6 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sat, 23 Sep 2023 01:53:00 +0300 Subject: [PATCH 30/30] Forbid get_name for arrays --- include/boost/pfr/detail/core_name20_static.hpp | 8 ++++++++ .../fields_names_get_name_on_array.cpp | 17 +++++++++++++++++ .../fields_names_names_as_array_on_array.cpp | 17 +++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/core_name/compile-fail/fields_names_get_name_on_array.cpp create mode 100644 test/core_name/compile-fail/fields_names_names_as_array_on_array.cpp diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 7334530..e54d95e 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -206,6 +206,10 @@ constexpr std::string_view get_name() noexcept { !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( + !std::is_array::value, + "====================> Boost.PFR: It is impossible to extract name from old C array since it doesn't have named members" + ); 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." @@ -220,6 +224,10 @@ constexpr auto tie_as_names_tuple() noexcept { !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( + !std::is_array::value, + "====================> Boost.PFR: It is impossible to extract name from old C array since it doesn't have named members" + ); 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." diff --git a/test/core_name/compile-fail/fields_names_get_name_on_array.cpp b/test/core_name/compile-fail/fields_names_get_name_on_array.cpp new file mode 100644 index 0000000..78f48d9 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_get_name_on_array.cpp @@ -0,0 +1,17 @@ +// 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 + +int main() { + (void)boost::pfr::get_name<0, int[10]>(); // Must be a compile time error +} + + diff --git a/test/core_name/compile-fail/fields_names_names_as_array_on_array.cpp b/test/core_name/compile-fail/fields_names_names_as_array_on_array.cpp new file mode 100644 index 0000000..a509355 --- /dev/null +++ b/test/core_name/compile-fail/fields_names_names_as_array_on_array.cpp @@ -0,0 +1,17 @@ +// 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 + +int main() { + (void)boost::pfr::names_as_array(); // Must be a compile time error +} + +