c++ - Optionally safety-checked cast on possibly incomplete type -


pursuant simple, intrusively reference-counted object system, have template<typename t> class handle, meant instantiated subclass of countedbase. handle<t> holds pointer t, , destructor calls decref (defined in countedbase) on pointer.

normally, cause problems when trying limit header dependencies using forward declarations:

#include "handle.h"  class foo; // forward declaration  struct mystruct {     handle<foo> foo; // okay, but... };  void bar() {     mystruct ms; }   // ...there's error here, implicit ~mystruct calls     // handle<foo>::~handle(), wants foo complete     // type can call foo::decref(). solve this, have     // #include definition of foo. 

as solution, rewrote handle<t>::~handle() follows:

template<typename t> handle<t>::~handle() {     reinterpret_cast<countedbase*>(m_ptr)->decref(); } 

note i'm using reinterpret_cast here instead of static_cast, since reinterpret_cast doesn't require definition of t complete. of course, won't perform pointer adjustment me... long i'm careful layouts (t must have countedbase leftmost ancestor, must not inherit virtually, , on couple of unusual platforms, vtable magic necessary), it's safe.

what really nice, though, if layer of static_cast safety possible. in practice, definition of t usually complete @ point handle::~handle instantiated, making perfect moment double-check t inherits countedbase. if it's incomplete, there's not can do... if it's complete, sanity check good.

which brings us, finally, question: is there way compile-time check t inherits countedbase not cause (spurious) error when t not complete?

[usual disclaimer: i'm aware there potentially unsafe and/or ub aspects use of incomplete types in way. nevertheless, after great deal of cross-platform testing , profiling, i've determined practical approach given unique aspects of use case. i'm interested in compile-time checking question, not general code review.]

using sfinae on sizeof check whether type complete :

struct countedbase {     void decref() {} };  struct incomplete; struct complete : countedbase {};  template <std::size_t> struct size_tag;  template <class t> void decref(t *ptr, size_tag<sizeof(t)>*) {     std::cout << "static\n";     static_cast<countedbase*>(ptr)->decref(); }  template <class t> void decref(t *ptr, ...) {     std::cout << "reinterpret\n";     reinterpret_cast<countedbase*>(ptr)->decref(); }  template <class t> struct handle {     ~handle() {         decref(m_ptr, nullptr);     }      t *m_ptr = nullptr; };  int main() {     handle<incomplete> h1;     handle<complete> h2; } 

output (note order of destruction reversed) :

static reinterpret 

live on coliru

trying complete type not derive countedbase yields :

main.cpp:16:5: error: static_cast 'oops *' 'countedbase *' not allowed 

that being said, think more elegant (and more explicit) approach introduce class template incomplete<t>, such handle<incomplete<foo>> compiles reinterpret_cast, , else tries static_cast.


Comments

Popular posts from this blog

python - pip install -U PySide error -

arrays - C++ error: a brace-enclosed initializer is not allowed here before ‘{’ token -

cytoscape.js - How to add nodes to Dagre layout with Cytoscape -