Pointer-to-member, type descriptors and references

1.2k Views Asked by At

I'm working on a type descriptor project in C++11. The type descriptor's job is to know the types of every member in a class, it's size and it's offset from the base of an object. I don't support multiple inheritance as well as objects with virtual methods so I am making this a lot simpler for now. The goal is to be able to serialize and unserialize objects using the descriptor.

Note that this is a pet project to mess around with features like variadic templates, pointer to members and other features of C++ I am not familiar with, so there's no need to point me toward something like boost::archiving. :)

The way I actually register members is very similar to boost::python::class_'s way.

ClassDescriptor fooDesc( "Foo" );
fooDesc.addMember( "a", &Foo:: a );
fooDesc.addMember( "b", &Foo:: b );

// (abridged for clarity) :
template< typename ClassType, typename MemberType >
ClassDescriptor& ClassDescriptor::addMember(
  const char* name,
  MemberType ClassType::* member
)
{
   return addMember< MemberType >( name, reinterpret_cast< size_t >( &(((ClassType*)0)->*member)) );
}

Unfortunately, the pointer-to-member feature of C++ can't be used with references in C++, as I've learned earlier this week : https://stackoverflow.com/a/8336479/1074536, so I am not able to use &Foo::refToAndInt for example.

As far as how I am computing the offset of the member, I am not using the offset of macro since my classes will not always be PODs.

So since I can't use pointer-to-members for computing the offset of references, I thought I'd try :

&(((Foo*)nullptr)->refToAnInt)

but has it has been pointed out in another stack overflow thread, this is undefined behavior and on obviously on LLVM it crashes. :(

I'd like to avoid doing stuff like taking the previous member's offset, adding it's size and then somehow computing the padding required to align my next member, since it would look messy and error prone

So, I can't use both of those tricks and offsetof is only for PODs. Any suggestion on what I could try next, apart from my other horrible suggestion?

Thanks!

1

There are 1 best solutions below

1
Earth Engine On

According to the standard,

  1. Classes

    6 ... A trivially class is a class that has a trivial default constructor (12.1) and ... ...

    10 A POD struct is a class that is both a trivial class and ... ...

12.1 Constructors

5 ...

 A default default constructor for class X is defined as deleted if:

 ...

 - any non-static data member with no brace-or-equal-initializer is of

reference type,

 ...

 A default constructor is trivial if it is neither user-provided nor deleted

and if

 ...

 - for all the non-static data members of its class that are of class type(or

array thereof), each such class has a trivial default constructor.

For sure, any class that has a reference member cannot be POD unless a brace-or-equal-initializer exists, for example:

class A
{
    A &me=*this;
};

If this is the case, you may be able to create specific workaround.

For the problem of

&(((Foo*)nullptr)->refToAnInt)

You can try the following:

std::aligned_storage<sizeof(Foo), std::alignment_of<Foo>::value>::type storage;
&((static_cast<Foo *>(static_cast<void *>(&storage)))->refToAnInt);

Its behavior should be well defined.