mercredi 5 août 2015

Optionally safety-checked cast on possibly incomplete type


Pursuant to a simple, intrusively reference-counted object system, I have a template<typename T> class Handle, which is meant to be instantiated with a subclass of CountedBase. Handle<T> holds a pointer to a T, and its destructor calls DecRef (defined in CountedBase) on that pointer.

Normally, this would cause problems when trying to limit header dependencies by using forward declarations:

#include "Handle.h"

class Foo; // forward declaration

struct MyStruct {
    Handle<Foo> foo; // This is okay, but...
};

void Bar() {
    MyStruct ms;
}   // ...there's an error here, as the implicit ~MyStruct calls
    // Handle<Foo>::~Handle(), which wants Foo to be a complete
    // type so it can call Foo::DecRef(). To solve this, I have
    // to #include the definition of Foo.

As a solution, I rewrote Handle<T>::~Handle() as follows:

template<typename T>
Handle<T>::~Handle() {
    reinterpret_cast<CountedBase*>(m_ptr)->DecRef();
}

Note that I'm using reinterpret_cast here instead of static_cast, since reinterpret_cast doesn't require the definition of T to be complete. Of course, it also won't perform pointer adjustment for me... but as long as I'm careful with layouts (T must have CountedBase as its leftmost ancestor, must not inherit from it virtually, and on a couple of unusual platforms, some extra vtable magic is necessary), it's safe.

What would be really nice, though, would be if I could get that extra layer of static_cast safety where possible. In practice, the definition of T is usually complete at the point where Handle::~Handle is instantiated, making it a perfect moment to double-check that T actually inherits from CountedBase. If it's incomplete, there's not much I can do... but if it's complete, a sanity check would be good.

Which brings us, finally, to my question: Is there any way to do a compile-time check that T inherits from CountedBase which will not cause a (spurious) error when T is not complete?

[Usual disclaimer: I'm aware that there are potentially unsafe and/or UB aspects to the use of incomplete types in this way. Nevertheless, after a great deal of cross-platform testing and profiling, I've determined that this is the most practical approach given certain unique aspects of my use case. I'm interested in the compile-time checking question, not a general code review.]



via Chebli Mohamed

Aucun commentaire:

Enregistrer un commentaire