Incomputable
Incomputable

Reputation: 2208

CRTP with reinterpret_cast into target class

Background

I'd like facades to be applied on ad-hoc basis, and not baking them into the class itself. But I need to operate on the data, so I need this to be accessible from the facade. Here is a small example:

#include <array>
#include <iostream>

template <typename T>
struct x_getter
{
    friend T;

    double x() const
    {
        return (*real_self)[0];
    }

    void x(double new_x)
    {
        (*real_self)[0] = new_x;
    }

private:
    T* real_self = reinterpret_cast<T*>(this);
    x_getter() = default; //prevents accidental creation
};

struct coordinates : std::array<double, 3>, x_getter<coordinates>
{
    using std::array<double, 3>::array;
};


int main()
{
    coordinates origin{};
    std::cout << origin.x();
    origin.x(12.7);
    std::cout << ' ' << origin.x() << '\n';
}

It segfaults. Using something similar a while back, I got unlucky to be able to get away with it.

Question

How do I make this with type of target class be available in facade class?

My understanding of class layout

Somewhere inside of the object, in unordered manner, there are array and x_getter. By reinterpret_casting it, I'm trying to trick it to think that this is coordinates, but when it performs operator[], the offset which is used is a bit off, which goes outside of the object, thus segfaulting.

Upvotes: 3

Views: 275

Answers (1)

user7860670
user7860670

Reputation: 37600

The problem here is that reinterpret_cast does not work because this pointer does not point at the beginning of coordinates class since it inherits from array before inheriting from x_getter. The memory layout for this class looks like this:

coordinates
|- std::array<double, 3>
|- x_getter

When you use reinterpret_cast<T*>(this) the address stored in this pointer is the address of x_getter object, but you force compiler to assume that it is actually an address of coordinates object. So dereferencing such a pointer to derived class leads to all kinds of undefined behavior.

Typically CRTP should use static_cast inside of method:

double x() const
{
    return (*static_cast<TDerived const *>(this))[0];
}

Unlike reinterpret_cast, static_cast will properly adjust this pointer to correctly point at derived object.

Upvotes: 3

Related Questions