Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Callbacks from private CRTP bases

Tags:

c++

The following code doesn't work, as you can't static_cast from private base class.

Replacing the cast with a C-style cast works (although I originally thought this would invoke undefined behaviour apparently it does not, see this answer), but is rather ugly as it also allows you to bypass const-checking, etc. The other approach would be to make CRTPBase a friend, but that would expose all of Derived's private members.

Is there another way of writing this without using a C-style cast and without making CRTPBase a friend?

template<typename T>
struct CRTPBase {
    void callBase() {
        T * derived = static_cast<T*>(this);
        derived->publicMethod();
    }
};

struct Derived : private CRTPBase<Derived> {
    void callParent() { this->callBase(); }
    void publicMethod() {}
};

int main() {
    Derived d;
    d.callParent();
    return 0;
}
like image 444
jleahy Avatar asked Apr 21 '14 16:04

jleahy


2 Answers

I think the best solution is to avoid private inheritance and instead opt for data hiding. Marking the member function protected will prevent access from everywhere except derived classes. A further bonus public inheritance is used instead.

template<typename T>
class CRTPBase {
protected:
  void callBase() {
    T * derived = static_cast<T*>(this);
    derived->publicMethod();
  }
};

struct Derived : public CRTPBase<Derived> {
  void callParent() { this->callBase(); }
  void publicMethod() {}
};

int main() {
  Derived d;
  d.callParent();
  d.callBase() // <- illegal
  return 0;
 }
like image 92
Alexander Oh Avatar answered Oct 19 '22 09:10

Alexander Oh


Not an ideal solution, but you can restrict the friendship to a unique method as follow:

template<typename T>
struct CRTPBase {
    friend T; // So T can see asDerived.
    void callBase() { asDerived()->publicMethod(); }
private:
    T* asDerived() { return static_cast<T*>(this); }
};

struct Derived : private CRTPBase<Derived> {
    friend Derived* CRTPBase<Derived>::asDerived();
    void callParent() { this->callBase(); }
    void publicMethod() {}
};
like image 24
Jarod42 Avatar answered Oct 19 '22 09:10

Jarod42