Question

I have the need to use offsetof from a template with a member selector. I've come up with a way, if you'll excuse the awkward syntax:

template <typename T,
          typename R,
          R T::*M
         >
constexpr std::size_t offset_of()
{
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M));
};

Usage isn't perfect (annoying at best):

struct S
{
    int x;
    int y;
};

static_assert(offset_of<S, int, &S::x>() == 0, "");
static_assert(offset_of<S, int, &S::y>() == sizeof(int), "");

The non-constexpr form is easier to use:

template <typename T, typename R>
std::size_t offset_of(R T::*M)
{
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M));
};

at the obvious disadvantage that it isn't done at compile-time (but easier to use):

int main()
{
    std::cout << offset_of(&S::x) << std::endl;
    std::cout << offset_of(&S::y) << std::endl;
}

What I'm looking for is syntax like the non-constexpr variety, but still fully compile-time; however, I can't come up with the syntax for it. I would also be happy with an offset_of<&S::x>::value (like the rest of the type traits), but can't figure out the syntax magic for it.

Was it helpful?

Solution

The following should work (credits go to the answer to this question for the idea):

#include <cstddef>

template <typename T, typename M> M get_member_type(M T::*);
template <typename T, typename M> T get_class_type(M T::*);

template <typename T,
          typename R,
          R T::*M
         >
constexpr std::size_t offset_of()
{
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M));
}

#define OFFSET_OF(m) offset_of<decltype(get_class_type(m)), \
                     decltype(get_member_type(m)), m>()

struct S
{
    int x;
    int y;
};

static_assert(OFFSET_OF(&S::x) == 0, "");

Note that in gcc, the offsetof macro expands to a builtin extension which can be used at compile time (see below). Also, your code invokes UB, it dereferences a null pointer, so even if it might work in practice, there are no guarantees.

#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)

As pointed out by Luc Danton, constant expressions cannot involve a reinterpret_cast according to the C++11 standard although currently gcc accepts the code (see the bug report here). Also, I found defect report 1384 which talks about making the rules less strict, so this might change in the future.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top