Search code examples
c++templatesapi-designcrtptemplate-templates

Simplifying API of classes extending each other by CRTP


I want to write class that extends multiple classes by (CRTP).

I can only get Extension<Base<Extension>> my_object; to work.

The api that I want is: Extension<Base> my_object;

How to make this api work?

Thanks.

Test (code is also at godbolt.org):

#include <iostream>

template <template<typename...> class Extension>
class Base1 : public Extension<Base1<Extension>> {
public:
    static void beep() { std::cout << "Base1 "; }
};

template <class Plugin>
class Extension1 {
public:
    Extension1() : plugin_(static_cast<Plugin*>(this)) {}
    void beep() {
        plugin_->beep();
        std::cout << "Extension1\n";
    }
private:
    Plugin* plugin_;
};

template <template<typename...> class Plugin>
class Extension2 {
public:
    Extension2() : plugin_(static_cast<Plugin<Extension2>*>(this)) {}
    void beep() {
        plugin_->beep();
        std::cout << "Extension2\n";
    }
private:
    Plugin<Extension2>* plugin_;
};

int main() {
    // This works.
    Extension1<Base1<Extension1>>b;
    b.beep();
    // This doesn't work.
    Extension2<Base1> c;
    c.beep();
    return 0;
}

Solution

  • One problem is that the template parameter to Extension2 does not match the signature that Base1 has. Another is that Extension2 does not match the parameter type expected by Base1.

    If you change the definition of Extension2 to propertly accept Base1, it itself is still not a candidate to be passed to Base1. You can workaround that with an inner template class that does match what Base1 expects. This inner class would look a lot like Extension1.

    template <template<template<typename...> class> class Plugin>
    class Extension2 {
        template <class P>
        struct Inner {
            Inner () : plugin_(static_cast<P *>(this)) {}
            void beep() { plugin_->beep(); }
        private:
            P* plugin_;
        };
    public:
        Extension2() {}
        void beep() {
            plugin_.beep();
            std::cout << "Extension2\n";
        }
    private:
        Inner<Plugin<Inner>> plugin_;
    };