Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr Offsetof with pointer to member data

Tags:

c++

Just before this question is being dismissed for being a duplicate, most (if not all) questions have accepted-answers with outdated / undefined behavior solutions.

The question:

Is there a way to get at, compile-time, the offset of a pointer to member data that:

  • does not rely on undefined behavior (nullptr things)
  • Works on the latest gcc version (the gcc trunk in compiler explorer)
  • works the same way as offsetof (or similarly, like the proposed code do).

I do not care about:

  • non standard-layout types
  • msvc compatibility. (it could be nice, but optional)
  • having a special case for a specific compiler

The issue:

With the trunk version of GCC, the following trick (hack) does not work:

#include <cstdint>
#include <cstddef>

namespace detail
{
    // The union stuff is for CLang to avoid creating warnings
    template<typename T, auto MPtr>
    struct offsetof_ptr_helper
    {
        union helper_t { int i = 0;  T value; };
        static constexpr helper_t v = {};
        static constexpr size_t sz = sizeof
        (uint8_t[
            (uint8_t *)&(v.value.*MPtr) -
            (uint8_t *)&v.value
        ]);
    };
}

template<typename T, auto MPtr>
static constexpr size_t offsetof_ptr()
{
    return detail::offsetofptr_helper<T, MPtr>::sz;
}

size_t f()
{
    struct X { char c[10]; int a; };
    return offsetof_ptr<X, &X::a>();
}

To be clear, this is a hack using C++98 features and this is not a real surprise that its support is being removed by latest compilers. It works on clang (using the -std=gnu++17 flag) and gcc (including/up-to g++ 7.3 using the flag -std=c++17).

like image 762
neam Avatar asked Apr 28 '18 20:04

neam


1 Answers

If you jump to the definition of offsetof in VS, it gives you this:

#define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))

Modified a bit to match your problem:

template<typename T, auto MPtr>
static constexpr size_t offsetof_ptr()
{
    return ((::size_t) & reinterpret_cast<char const volatile&>((((T*)0)->*MPtr)));
}

size_t f()
{
    struct X { char c[10]; int a; };
    return offsetof_ptr<X, &X::a>();
}

This compiles on GCC and MSVC. (It requires C++17 because of the template auto parameter.)

like image 130
SteakOverflow Avatar answered Oct 17 '22 13:10

SteakOverflow