Skip to content

Commit

Permalink
Parsing ala boost type_index
Browse files Browse the repository at this point in the history
  • Loading branch information
denzor200 committed Aug 28, 2023
1 parent efd25e9 commit 97a1e46
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 20 deletions.
4 changes: 2 additions & 2 deletions include/boost/pfr/detail/core_name14_disabled.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ template <class T, std::size_t I>
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;
Expand All @@ -31,7 +31,7 @@ template <class T>
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();
Expand Down
102 changes: 84 additions & 18 deletions include/boost/pfr/detail/core_name20_static.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,96 @@

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};
}

template <typename MsvcWorkaround, auto ptr>
consteval auto name_of_field_impl() noexcept {
#ifdef _MSC_VER
constexpr auto sv = __FUNCSIG__;
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 <bool Condition>
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_USER_DEFINED_PARSING to correct values. See section "
"'TODO: name of the section' of the documentation for more information."
);
}

template <class T>
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_USER_DEFINED_PARSING to correct value after that."
);
}

consteval std::string_view name_of_field_parse(std::string_view sv) noexcept {
#if defined(BOOST_PFR_CORE_NAME_USER_DEFINED_PARSING)
constexpr auto s = detail::make_core_name_skip BOOST_PFR_CORE_NAME_USER_DEFINED_PARSING;
#elif defined(_MSC_VER)
// 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__;
constexpr auto s = detail::make_core_name_skip(52, 16, "->");
#elif defined(__clang__)
// 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__;
constexpr auto s = detail::make_core_name_skip(64, 2, ".");
#elif defined(__GNUC__)
// 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, "::");
constexpr auto s = detail::make_core_name_skip(79, 2, "::");
#else
// Deafult code for other platforms... Just skip nothing!
constexpr auto s = detail::make_core_name_skip(0, 0, "");
#endif
return detail::apply_core_name_skip(sv, s);
}

template <typename MsvcWorkaround, auto ptr>
consteval auto name_of_field_impl() noexcept {

#if defined(BOOST_PFR_FUNCTION_SIGNATURE)
constexpr auto fn = detail::name_of_field_parse(BOOST_PFR_FUNCTION_SIGNATURE);
detail::assert_compile_time_legths<!fn.empty()>();
#elif defined(__FUNCSIG__)
constexpr auto fn = detail::name_of_field_parse(__FUNCSIG__);
detail::assert_compile_time_legths<!fn.empty()>();
#elif defined(__PRETTY_FUNCTION__) \
|| defined(__GNUC__) \
|| defined(__clang__)
constexpr auto fn = detail::name_of_field_parse(__PRETTY_FUNCTION__);
detail::assert_compile_time_legths<!fn.empty()>();
#else
constexpr std::string_view fn = "";
detail::failed_to_get_function_name<MsvcWorkaround>();
#endif

auto res = std::array<char, fn.size()+1>{};
std::ranges::copy(fn, res.begin());
return res;
Expand Down
1 change: 1 addition & 0 deletions test/core_name/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ test-suite pfr_tests
[ run fields_names_constexpr.cpp : : : : ]
[ run fields_names_nonascii.cpp : : : <toolset>msvc:<cxxflags>"/utf-8" : ]
[ run fields_names_big.cpp : : : <toolset>msvc:<cxxflags>"/bigobj" : ]
[ run print_name.cpp : : : <test-info>always_show_run_output ]
;


33 changes: 33 additions & 0 deletions test/core_name/print_name.cpp
Original file line number Diff line number Diff line change
@@ -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 <iostream>

// This cpp file:
// * tests BOOST_PFR_CORE_NAME_USER_DEFINED_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_USER_DEFINED_PARSING (0,0,false,"")
#include <boost/pfr/core_name.hpp>

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;
}

0 comments on commit 97a1e46

Please sign in to comment.