From dbbfa6ea7f35f30d3ec50955fb58dfb64aa6a84a Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 3 Sep 2023 02:12:25 +0300 Subject: [PATCH] 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(); +} +