Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to calculate offset of a class member at compile time?

Given a class definition in C++

class A
{
  public:
    //methods definition
    ....

  private:
    int i;
    char *str;
    ....
}

Is it possible to calculate the offset of a class member at compile time using C++ template meta-programming? The class is not POD, and can have virtual methods, primitive and object data member.

like image 736
Jun Yuan Avatar asked Nov 01 '12 15:11

Jun Yuan


Video Answer


2 Answers

Based on Matthieu M.'s answer but shorter and with no macros:

template<typename T, typename U> constexpr size_t offsetOf(U T::*member)
{
    return (char*)&((T*)nullptr->*member) - (char*)nullptr;
}

And it's called like this:

struct X { int a, b, c, d; }

std::cout << "offset of c in X == " << offsetOf(&X::c);

Edit:

Jason Rice is correct. This will not produce an actual constant expression in C++11. It doesn't look possible given the restrictions in http://en.cppreference.com/w/cpp/language/constant_expression -- in particular no pointer difference and reinterpret_castcan be in a constant expression.

like image 64
Glen Low Avatar answered Sep 20 '22 10:09

Glen Low


Well... in C++11, you actually can compute such offsets right with regular C++ facilities (ie, without delegating to a particular compiler intrinsic).

In action at liveworkspace:

template <typename T, typename U>
constexpr int func(T const& t, U T::* a) {
     return (char const*)&t - (char const*)&(t.*a);
}

However this relies on t being a reference to a constexpr instance here, which might not be applicable to all classes. It does not forbid T from having a virtual method though, nor even a constructor, as long as it is a constexpr constructor.

Still, this is quite a hindrance. In unevaluated contexts we could actually use std::declval<T>() to simulate having an object; while having none. This poses no specific requirements on the constructor of an object, therefore. On the other hand, the values we can extract from such context are few... and they do pose issues with current compilers, too... Well, let's fake it!

In action at liveworkspace:

template <typename T, typename U>
constexpr size_t offsetof_impl(T const* t, U T::* a) {
    return (char const*)t - (char const*)&(t->*a) >= 0 ?
           (char const*)t - (char const*)&(t->*a)      :
           (char const*)&(t->*a) - (char const*)t;
}

#define offsetof(Type_, Attr_)                          \
    offsetof_impl((Type_ const*)nullptr, &Type_::Attr_)

The only issue I foresee is with virtual inheritance, because of its runtime placement of the base object. I would be glad to be presented with other defects, if there are.

like image 40
Matthieu M. Avatar answered Sep 20 '22 10:09

Matthieu M.