From 4a1defaaeb88d001b060d07ccb23559c102cb287 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 25 Jun 2023 22:40:31 +0000 Subject: [PATCH] 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() {} +