7
template<class T, typename U> ptrdiff_t foo(T U::* m)
{
    // return offset
}

How I can get the offset of the field 'm' in this context? I would prefer to use am compile-time expression.

Thanks in advance for any help. Best regards

0xbadf00d
  • 17,405
  • 15
  • 67
  • 107
  • This is probably a bad idea. Why not use a pointer-to-member type instead? – GManNickG Apr 11 '11 at 06:40
  • Hum, what exactly do you mean? The parameter above IS a pointer-to-member ... – 0xbadf00d Apr 12 '11 at 06:32
  • Right, and leave it at that. Offsets are simply too primitive to be used for most C++ classes (non-POD's). – GManNickG Apr 12 '11 at 06:47
  • Okay, but you had written 'why not use a pointer-to-member type INSTEAD? How do you mean that? Btw.: I could write a compiler specific version of my member_offset function. – 0xbadf00d Apr 12 '11 at 14:06
  • It's impossible for me to suggest what you should use a pointer-to-member for instead, because you've only [asked the step and not the goal](http://www.catb.org/esr/faqs/smart-questions.html). What are you trying to accomplish? – GManNickG Apr 12 '11 at 17:08
  • 2
    @GManNickG Vulkan, OpenGL and any other graphics libraries that use structured buffers asks for offsets instead of pointers-to-member. In that case, they don't understand pointers-to-member, and you have to translate that into an offset. – JoaoBapt Mar 16 '21 at 18:37

4 Answers4

7

@Michael J

Thanks for your answer. This wasn't exactly what I was looking for, but it gives me the inspiration to doing that:

template<class T, typename U>
std::ptrdiff_t member_offset(U T::* member)
{
    return reinterpret_cast<std::ptrdiff_t>(
        &(reinterpret_cast<T const volatile*>(NULL)->*member)
    );
}
0xbadf00d
  • 17,405
  • 15
  • 67
  • 107
  • 4
    You are dereferensing a null pointer here. That is not allowed, unless you are implementing the library an get a special dispensation for use in the offsetof macro. Someone beat me to the -1. – Bo Persson Apr 11 '11 at 07:16
  • That's what the offsetof macro does: (size_t)&reinterpret_cast((((s *)0)->m)). What's the difference? – 0xbadf00d Apr 11 '11 at 09:26
  • 4
    The difference is that offsetof does that for a **particual compiler**, and only beacuse the library designer has made a special deal with the compiler writer. Then it might work. On other compilers offsetof uses as special __builtin_offsetof function instead. Or some other trick. The main reason for having an offsetof in the library is that you cannot write it portably, you need special support from the compiler. – Bo Persson Apr 11 '11 at 11:16
  • Like I said in a comment above, `offsetof` and kin are bad ideas in C++. Use pointer-to-members. – GManNickG Apr 11 '11 at 16:09
3

Sounds like you're looking for the offsetof() macro.

Michael J
  • 7,631
  • 2
  • 24
  • 30
  • 3
    It is not legal to call `offsetof` with a member pointer, only a member name. (in many implementations it may work, but given how obscure this kind of thing is, it could break in any compiler updates) – Yakk - Adam Nevraumont Mar 09 '14 at 17:00
  • Hi @Yakk. I'm not quite sure what you mean. If you click on the word "offsetof" (above) it takes you to the docs for the macro. It looks kosher to me. By a "member pointer" to you mean a member of a struct that is a pointer? e.g. struct s { int i; char *p; }; size_t n = offsetof(struct s, p); – Michael J Mar 09 '14 at 18:10
  • You cannot use `offsetof()` if the pointer to member is a template argument. – Florian Winter Oct 14 '22 at 20:28
2

You can get the offset, but it's not free:

template <typename T, class C>
size_t memberOffset(T C::*member)
{
    C c {};
    return size_t(&(c.*member)) - size_t(&c);
}

// usage
struct Vector {
    int x;
    int y;
};

size_t off = memberOffset(&Vector::y);

Unfortunately as you can see, this is not a constexpr, and so it can't be used in all scenarios you may want to. It also has a (very small) overhead, but it seems that the compiler just completely optimizes it out: https://godbolt.org/z/jGeox9.

If you are wondering if you can just sprinkle constexpr everywhere yourself and make this work, you can, and your compiler might even compile it and run, but using a cast to size_t is not valid in a constexpr despite the known defect that many compilers allow it.

Credits for this method do not belong to me, but to Dale Weiler and this excellent gist: https://gist.github.com/graphitemaster/494f21190bb2c63c5516

Asad-ullah Khan
  • 1,573
  • 18
  • 22
  • As you mentioned this is not constexpr (Visual Studiio can't compile it for example). However I made a request for another built-in for this: https://developercommunity.visualstudio.com/t/Please-add-constexpr-builtin-for-getting/10391992? – Somnium Jun 15 '23 at 11:52
0

The simple answer is that you can't. If the type U is a POD, you can use the macro offsetof, but formally, it's undefined behavior if the type isn't a POD: depending on the compiler, you'll get a compile time error, or simply wrong results some of the time. And you can't use it on a pointer to member. You have to invoke it with the name of the class, and the name of the member.

James Kanze
  • 150,581
  • 18
  • 184
  • 329