Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting pointer-to-member to member index using Boost.PFR without creating an object

Consider the following struct:

struct Particle
{
    float scale;
    float opacity;
    float rotation;
};

I want the following to compile:

static_assert(indexOf<&Particle::scale>() == 0);
static_assert(indexOf<&Particle::opacity>() == 1);
static_assert(indexOf<&Particle::rotation>() == 2);

I have managed to implement a working version using Boost.PFR:

template <auto PM>
consteval int indexOf()
{
    using ClassType = typename PMTraits<decltype(PM)>::ClassType;
    constexpr ClassType obj{};

    std::size_t result = -1;

    [&]<auto... Is>(std::index_sequence<Is...>)
    {
        (...,
            (
                (    static_cast<const void*>(&(boost::pfr::get<Is>(obj))) 
                == static_cast<const void*>(&(obj.*PM))                  ) 
                ? (result = Is) : 0
            )
        );    
    }(std::make_index_sequence<boost::pfr::tuple_size_v<ClassType>>{});

    return result;
}

But I am displeased by the fact that I need to create an object of type ClassType in order to make obj.*PM work.

Is there a better way of doing this that (1) does not require the creation of an object at compile-time and/or (2) is more efficient to compile?

like image 963
Vittorio Romeo Avatar asked Oct 23 '25 03:10

Vittorio Romeo


1 Answers

You can declare an extern variable to obtain constexpr reference without actually instantitate the object. Also explicitly cache std::array<void const*, N> ptrs<T> so boost::pfr::structure_tie is run only once per struct type.

https://godbolt.org/z/M6KEdbo3n

template <class T>
struct fake_object_wrapper { T const value; };

template <class T>
extern const fake_object_wrapper<T> fake_object;

template<class T>
constexpr auto ptrs = std::apply([](auto const&... ref) {
    return std::array{static_cast<void const*>(std::addressof(ref))...};
}, boost::pfr::structure_tie(fake_object<T>.value));

template<auto MemPtr>
constexpr size_t index_of = []<class T, class V>(V T::* p) {
    return std::find(ptrs<T>.begin(), ptrs<T>.end(),
        std::addressof(fake_object<T>.value.*p)) - ptrs<T>.begin();
}(MemPtr);

////

struct Particle {
    float scale;
    float opacity;
    float rotation;
};

static_assert(index_of<&Particle::scale> == 0);
static_assert(index_of<&Particle::opacity> == 1);
static_assert(index_of<&Particle::rotation> == 2);

Note that this will fail if you have [[no_unique_address]] members in your struct.

like image 67
Weeekly Avatar answered Oct 26 '25 12:10

Weeekly



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!