offsetof()
macro with a C++ class, and in an attempt to avoid a warning, I stumbled over the following, to my eyes, strange behavior.Let's start with the following code:
class MyClass { public: MyClass() { ... } size_t get_offset() { return offsetof(MyClass, y); } // Gives warning private: int x,y,z; };When compiling, I get the warning
offsetof.cc: In member function ‘int MyClass::get_offset() const’: offsetof.cc:14: warning: invalid access to non-static data member ‘MyClass::y’ of NULL object offsetof.cc:14: warning: (perhaps the ‘offsetof’ macro was used incorrectly)The source of the problem is that
offsetof()
macro can only be used with POD structures, i.e., from Chapter 9 paragraph 4 in ISO/IEC 14882: 2003:
A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor.Cool, so let's move all the members to a base class and inherit from it instead. That will make the base class a POD, so we can get the offset of the member from there and just add the other stuff in the subclass.
struct Base { int x,y,z; }; class MyClass : public MyBase { public: MyClass() { ... } size_t get_offset() { return offsetof(MyBase, y); } };... and as a result, I got no warnings! Very nice.
Aw, shoot, I cannot have x
, y
, and z
public, I'd better make them protected so that MyClass
can work with them, but they are not available to anybody else (encapsuling the state like a good programmer):
struct Base { protected: int x,y,z; }; class MyClass : public MyBase { public: MyClass() { ... } size_t get_offset() { return offsetof(MyBase, y); } };... and then let's just compile it and off we go:
$ g++ -ggdb -Wall -ansi -pedantic offsetof.cc -o offsetof offsetof.cc: In member function ‘int MyClass::get_offset() const’: offsetof.cc:8: error: ‘int MyBase::y’ is protected offsetof.cc:16: error: within this context offsetof.cc:16: warning: invalid access to non-static data member ‘MyBase::y’ of NULL object offsetof.cc:16: warning: (perhaps the ‘offsetof’ macro was used incorrectly)Hey! What is going on now! Just making the member variables protected does not make the struct non-POD... or? Well, it turns out that it actually does, let us read that paragraph again (with the boldface emphasis added by me):
A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor.So, what is an aggregate class then? Moving on to 8.5.1/1, we have:
An aggregate is an array or a class with no use-declared constructors, no private or protected non-static data members, no base classes, and no virtual functions.So, making the members protected actually made the base class non-POD. Shoot... now what? Well, that restriction only applies to the
MyBase
class, not to the way we inherit from that class, so by just using protected inheritance, I will make the member variables protected within MyClass
while MyBase
is a POD, like this:
struct MyBase { int x,y,z; }; class MyClass : protected MyBase { public: MyClass() { x = 1; y = 2; z = 3; } int get_offset() const { return offsetof(MyBase, y); } };This also means that I finally found a good reason for protected inheritance, which is one of the language gadgets I really never have not seen any use for until now.