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.
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.