Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static assertion that a base pointer "equals" a derived pointer

Suppose I have a polymorphic hierarchy of classes with a common base:

struct Base { ~virtual Base() = default; };

I have inherited a large codebase which contains a long series of accessor functions (one for each derived type) that obtain a derived pointer via reinterpret_cast:

Derived * Get()       // Derived inherits from Base
{
    Base * b = lookup_derived();
    return reinterpret_cast<Derived *>(b);
}

Obviously the code should be using static_cast or dynamic_cast (depending on whether the base is virtual). However, because this translation unit is large and the definitions of all the derived classes are jointly huge, the TU does not contain the definition of Derived, but only its declaration.

I would like to make this code more robust by adding to each derived class definition a static assertion that this reinterpret cast will produce the correct result. Essentially, I want something like this:

struct Derived : Base
{
    static_assert(static_cast<Derived *>(std::declval<Base *>()) ==
                  reinterpret_cast<Derived *>(std::declval<Base *>()));

    // ...
};

This constructions does not work of course since declval must not be evaluated, and the result of a reinterpret cast is not a constant expression. Is there any standard C++ mechanism to perform such a test statically? (The alternative would be a runtime check on this in the class constructor.)

Edit: Based on Aaron's post, I struck me that the question could be phrased entirely without reinterpret_casts:

static_cast<void *>(std::declval<Derived *>()) ==
static_cast<void *>(static_cast<Base *>(std::declval<Derived *>()))
like image 232
Kerrek SB Avatar asked Oct 18 '25 14:10

Kerrek SB


1 Answers

I'm not sure if this will be good enough for the setup you have, but this code on ideone works for me (clang 3.5.0 and g++ 4.9.3).

Updated to cast in the other direction, i,e. Derived*-to-Base*, to more closely match the question. And also updated to use static_cast<void*> explicitly instead of C-style casting and reinterpret_cast .

template<typename D, typename B>
struct CheckCasting { 
    static D d_static;

    constexpr
    static B* bp = &d_static; // the original object is D,
                              // that allows us to static_cast both ways

    static
    constexpr bool static_equals_reinterpret() { 
        return static_cast<void*>(static_cast<D*>(bp)) 
            == static_cast<void*>(                bp );
    } 
};

struct Base { 
    constexpr Base() : i(0) {}
    int i;
};

struct derived_with_virtual : public Base { 
    derived_with_virtual() {}
    virtual void foo() { } 
};
struct without_virtual : public Base { 
    without_virtual() {}
};

static_assert( ! CheckCasting<derived_with_virtual, Base> :: static_equals_reinterpret()   ,"");
static_assert(   CheckCasting<without_virtual     , Base> :: static_equals_reinterpret()   ,"");

A few comments:

  • I tried to replace the cast to void* with reinterpret_cast, but clang didn't like that. "note: reinterpret_cast is not allowed in a constant expression".
  • I tried moving the static_assert inside the Derived class without success.
  • This requires statically allocating an object for each Derived class.
like image 161
Aaron McDaid Avatar answered Oct 21 '25 02:10

Aaron McDaid