Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GIve away the details of get_name #150

Closed
akrzemi1 opened this issue Nov 28, 2023 · 9 comments
Closed

GIve away the details of get_name #150

akrzemi1 opened this issue Nov 28, 2023 · 9 comments

Comments

@akrzemi1
Copy link
Member

Would it be possible to describe in "How it works" how obtaining the name is implemented?

I tried to study the library for a couple of hours, and still cannot understand it. Apparently, you must be creating a pointer-to-member somewhere, no? But how is it done?

@bansan85
Copy link

bansan85 commented Nov 29, 2023

You need to pass the field in a template function. Then you use a demangle function inside the current template function.

To demangle, use the special feature from the preprocessor: __PRETTY_FUNCTION__ or __FUNCSIG__.

#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

Then you need to extract the template parameter of the current template function. But the output of the demangle function is not standardized, it's compiler dependent. See BOOST_PFR_CORE_NAME_PARSING

#ifndef BOOST_PFR_CORE_NAME_PARSING
# if defined(_MSC_VER)
# 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__)
# define BOOST_PFR_CORE_NAME_PARSING (sizeof("auto boost::pfr::detail::name_of_field_impl() [MsvcWorkaround = ") - 1, sizeof("}]") - 1, backward("."))
# elif defined(__GNUC__)
# define BOOST_PFR_CORE_NAME_PARSING (sizeof("consteval auto boost::pfr::detail::name_of_field_impl() [with MsvcWorkaround = ") - 1, sizeof(")]") - 1, backward("::"))
# else
// Default parser for other platforms... Just skip nothing!
# define BOOST_PFR_CORE_NAME_PARSING (0, 0, "")
# endif
#endif

You may have a look at https://bitwizeshift.github.io/posts/2021/03/09/getting-an-unmangled-type-name-at-compile-time/

And after writing it, I noticed it returns only the type, not the member.... I hope it will help you a little... I also spend an hour without understand how the name of the field is demangled by __PRETTY_FUNCTION__.

@denzor200
Copy link
Contributor

#90
It was borned here in the comments :)

@akrzemi1
Copy link
Member Author

Thanks.
Bu actually the hard part that I struggle with is how you obtain a member, or a pointer to member. PFR so far only returned tuples of references, and I still do not see how you get from there to the "member", or a pointer to one.

@schaumb
Copy link

schaumb commented Nov 29, 2023

(There are no pointer-to-member in this method (I tried to get here but constexpr time will not work).)

The simplified strategy is the following:

  • We create a not existing extern object (code)
template<class T>
extern const T non_exists;
  • We use the structure binding constexpr time on this object to get the members by their pointers (code)
template<class T>
constexpr auto get_members() {
    // this example works only with 3 member
    auto& [m1, m2, m3] = non_exists<T>;
    return std::tuple{&m1, &m2, &m3};
}
  • We pass these objects to a template argument (code)
template<class T, std::size_t I, auto* member = std::get<I>(get_members<T>())>
auto get_name()
  • We generates the function structure with compiler specific macro, and we process the textual representation which contains the member template argument with it's value (code)
{
    __PRETTY_FUNCTION__ // this contains:
   // member = &non_exists<the_type>.member_name
}

So we "get" the name from the non-existent object's member address.
This is works because the non_exists object is not used runtime and T class is not instantiated.


The library uses some workaround, but basically, this is under the hood

@akrzemi1
Copy link
Member Author

We use the structure binding constexpr time on this object to get the members by their pointers (code)

template<class T>
constexpr auto get_members() {
    // this example works only with 3 member
    auto& [m1, m2, m3] = non_exists<T>;
    return std::tuple{&m1, &m2, &m3};
}

I still don't get this part. This code only returns pointers (rather than pointers-to-members). boost::pfr::detail::sequence_tuple::get returns a reference, like int const&. How can you turn it into a member pointer, or something that you could get a name from?

@denzor200
Copy link
Contributor

denzor200 commented Nov 29, 2023

You right, only references in this code. And it's more than enough to obtain the name.
Looks at this: https://godbolt.org/z/31T817Mef
No pointer to members in the snippet, only regular pointer(of course, might be changed to reference).
That's possible only since C++20, looks at the section "Template non-type arguments" here: https://en.cppreference.com/w/cpp/language/template_parameters

@akrzemi1
Copy link
Member Author

Oh. Thank you. This is brilliant.
I think this explanation is worth putting in the docs. This is very interesting on its own, and would contribute to Boost being not only good libraries but also the source to learn interesting programming techniques.

@denzor200
Copy link
Contributor

Sure, article in the docs will be created later

@XRay3D
Copy link

XRay3D commented Dec 16, 2023

There are no pointer-to-member in this method (I tried to get here but constexpr time will not work).

https://godbolt.org/z/Kj9MKosz9
Perhaps line 14 like this is faster at compile time.

constexpr auto first = sv.find_last_of(":.>", last);

than

constexpr auto first = sv.find_last_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789", last);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants