boostorg / pfr

std::tuple like methods for user defined types without any macro or boilerplate code
https://boost.org/libs/pfr
Boost Software License 1.0
1.34k stars 161 forks source link

GIve away the details of `get_name` #150

Closed akrzemi1 closed 9 months ago

akrzemi1 commented 12 months ago

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 commented 12 months ago

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__. https://github.com/boostorg/pfr/blob/c695aa0b32ea7665d9e76cfa25655ed6302508e5/include/boost/pfr/detail/core_name20_static.hpp#L88-L96

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 https://github.com/boostorg/pfr/blob/c695aa0b32ea7665d9e76cfa25655ed6302508e5/include/boost/pfr/config.hpp#L115-L126

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 commented 12 months ago

https://github.com/boostorg/pfr/issues/90 It was borned here in the comments :)

akrzemi1 commented 12 months ago

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 commented 12 months ago

(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:

template<class T>
extern const T non_exists;
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};
}
template<class T, std::size_t I, auto* member = std::get<I>(get_members<T>())>
auto get_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 commented 12 months ago

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 commented 12 months ago

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 commented 12 months ago

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 commented 12 months ago

Sure, article in the docs will be created later

XRay3D commented 11 months ago

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