2

Taking offset of a data member is as easy as this:

#define MEMBER_OFFSET(Type, Member) \
    ((unsigned long)(((char *)&((Type *)0)->Member) - (char *)0));

I want to make this a constant compile-time expression (or use type traits). For example, to use it to implement SFINAE based solutions using member offsets, use it static assertions etc.

UPDATE: The question is - how to make it a compile-time expression. Not whether it works with POD types, or is there a standard macro in C library etc.

  • 2
    Of course we should only use this macro on POD types. (Though that last cast should be `ptrdiff_t`, not `unsigned long`. Not to mention that `offsetof` already exists in ``.) – GManNickG Feb 04 '11 at 15:07
  • 1
    @Vlad.... brrrrrr.... and... you said "as easy as".... urgh... :-( – Stephane Rolland Feb 04 '11 at 15:13
  • @GMan: There is trick to make it work for non-POD types. I just want to keep it simple. And the `offsetof` in standard C library does not support that. –  Feb 04 '11 at 15:24
  • @Vlad: I'd like to know that trick, if you don't mind. – GManNickG Feb 04 '11 at 15:31
  • @GMan: It is complicated, but this answer shows the idea - http://stackoverflow.com/questions/1129894/why-cant-you-use-offsetof-on-non-pod-strucutures-in-c You can use it with type traits, to figure out if it is non-POD and whether there is default constructor etc.. –  Feb 04 '11 at 15:40
  • @Vlad: Sorry, but I don't see anything in there that computes member offsets for non-POD's, without UB. – GManNickG Feb 04 '11 at 15:48
  • @Vlad: The reason everyone's talking about `offsetof` is that anything you write, including the example in the question, will be less portable and less safe than `offsetof`. – aschepler Feb 04 '11 at 19:14

3 Answers3

3

Though I can't get what your compiler is, the following code can be compiled by VC8, ideone(gcc-4.3.4), and Comeau online:

struct A { int i; };
template< size_t > struct S;

int main() {
  S< offsetof( A, i ) > *p;
}

Gcc has __offsetof__ extension. VC seems to have a capability to take a non-compile-time constant for a template argument strangely. As for Comeau, I have no idea about the internal of Comeau's offsetof unfortunately.

Incidentally, though this won't answer your question directly, as for SFINAE purpose, since a member pointer constant can be used as a template argument and you can specialize on it, you can write as the following:

struct A {
  int i, j;
};

template< int A::* > struct S;
template<> struct S< &A::i > { static char const value = 'i'; };
template<> struct S< &A::j > { static char const value = 'j'; };

int main() {
  cout<< S< &A::i >::value <<endl;
  cout<< S< &A::j >::value <<endl;
}

Hope this helps.

Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
  • What do you mean by "take a non-static constant for a template argument"? – aschepler Feb 04 '11 at 19:17
  • @aschepler: Sorry for my misleading wording. I mean compile-time constant. VC8 seems to expand `offsetof( A, i )` in the above code to `(size_t)&reinterpret_cast((((A*)0)->i))`. Probably this can't be used as a compile-time constant(template argument) normally. I edited the answer. – Ise Wisteria Feb 04 '11 at 19:32
  • @Ise: Strangely, Comeau allows a result of this expression to be a template argument. That is exactly what I need. Is that `__builtin_offsetof` in gcc a compile-time expression? I thought about converting pointers into integral types.. something like that, but can't think exactly how to do it.. –  Feb 04 '11 at 21:18
  • @VladLazarenko: I think gcc's `__builtin_offsetof` is compile-time constant as we expect. – Ise Wisteria Feb 04 '11 at 22:34
  • 1
    It turns out that GCC (version > 4) and, according to your answer, VC++ and Comeau has built-in support for offsetof so that it can be used in compile-time expressions. However, if you do magic with pointers yourself, it does not qualify as a constant expression. So I am accepting your answer as the best. –  Feb 10 '11 at 22:11
1

The C standard library already has offsetof that does what this attempts to (but you can use it without UB). Unfortunately, applying it to a non-POD type still gives undefined behavior, so for a lot of C++ it's still useless.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • @Jerry: `offsetof` from C standard library does not support POD types. Neither is my simple macro in example, but I usually use full-featured one on my code, which does. That is the reason why I stick to custom one. –  Feb 04 '11 at 15:27
  • @Vlad Lazerenko: What makes you think `offsetof` doesn't support POD types? – Jerry Coffin Feb 04 '11 at 15:28
  • @Jerry: http://stackoverflow.com/questions/1129894/why-cant-you-use-offsetof-on-non-pod-strucutures-in-c –  Feb 04 '11 at 15:32
  • 1
    @Vlad Lazeranko: That's specifically about **non-POD** types. `offsetof` works with POD types. Using it on a non-POD type gives UB -- which can include it's working, as seems to be what happened in that case. With a POD type, the results are well-defined though. – Jerry Coffin Feb 04 '11 at 15:39
  • @Jerry: That's what I am saying. There is a solution for non-POD types and I use that. It is C++-only and is too complicated to post in question. The question is still - how to make it compile-time expression, assuming that the type is POD? –  Feb 04 '11 at 15:44
  • @Vlad Lazerenko: I don't think you can. In fact, I'm pretty sure writing it yourself *always* gives undefined behavior. I'm also pretty sure that's true for both POD and non-POD types. – Jerry Coffin Feb 04 '11 at 15:56
0

First, its a bad idea to put the semicolon in the macro -- it cant be used in a large expression.

Second, its a bad idea to use unsigned long when there are perfectly good types specifically designed for pointers (namely size_t and ssize_t, provided in stdint.h). These types are especially useful when using 32 and 64 bit architectures -- and GCC has an extension to printf, "%zu", to use the correct word size.

G++ computes this at compile-time, at least with -O2 and -O3 with POD types

Foo Bah
  • 25,660
  • 5
  • 55
  • 79
  • g++ probably can compute that in compile time, but it certainly won't let you use a result of that computation as a compile-time constant, unfortunately. –  Feb 04 '11 at 21:15
  • You forgot ptrdiff_t ;-) Further I think you are not answering OP's question. An Integral Constant Expression is a well-defined term in the C++ standard for what OP is asking for. The question is NOT whether some offset calculation is optimized out by the compiler but how to derive a constant that works as a template nontype argument (probably in order to do so manually within a template-driven code generator). – Dude Oct 18 '12 at 01:33