lundi 30 novembre 2015

Can I automatically deduce child type in base class template method?

For a start, let me provide a bit context. I'm creating a small game framework for my game, where I have gameplay system and event system. I try to use language features such as templates to avoid boilerplate in user code.

There is Component class, from which other game elements are derived, i.e. Camera, Sprite, Button.

using EventType = unsigned int;
using EventCallback = std::function<void(Event)>;

class Component {
public:
    // This is public version for general functor objects,
    // particularly used with lambda
    void addHandler(EventType type, const EventCallback &callback);

protected:
    // Method for child classes. Creates event handler from method of the class.
    //
    // This is template method, because std::bind is used for functor creation,
    // which requires class type at compile time.
    template <class T>
    void addHandler(EventType type, void (T::*method)(Event event)) { /*...*/ }
private:
    // ...
};

Every component listens to particular set of events, so it doesn't have to implement handlers for every possible type of event. Also, component user should be able to add custom event listeners without creating new class for each game element, for example:

class Button : public Component {
public:
    Button() {
        addHandler(kOnTouch, &Button::onTouch);
    }
    // ...
};

Button ok, cancel;
ok.addHandler(kOnClick, [](Event) {
    // ...
});

cancel.addHandler(kOnClick, [](Event) {
    // ...
});

// Add another handler somewhere else in the code
cancel.addHandler(kOnClick, someCallback);

So, what I want to do is late binding, with compile-time check for member functions. I want to ensure that method pointer passed to addHandler() belongs to child class that called addHandler(). I can get type of method owner inside addHandler() with help of template argument deduction. But I didn't find a way to deduce child class type. Here how I tried to accomplish this with decltype(*this) and type traits:

template <class T>
void addHandler(EventType type, void (T::*method)(Event event)) {
    /***** This check is INCORRECT *****/
    // Check that T is same as child class
    using ChildClass = std::remove_reference<decltype(*this)>::type;
    static_assert(std::is_same<T, ChildClass>::value,
                  "Event handler method must belong to caller class");

    using namespace std::placeholders;

    EventHandler::Callback callback =
            std::bind(method, static_cast<T *>(this), _1);
    addHandler(EventHandler(type, callback));
}

Here we need child class type to compare T with. It seems that ChildClass being assigned to base Component class rather than to child. Is there a way to deduce child class type automatically, changing only inside of method-version addHandler()? It's important to template only this overloaded addHandler() and not the whole Component class to minimize generated code and be able to use polymorphism. So it is tiny wrapper around more generic addHandler(), taking std::function.

At the moment, I can only check that T is Component:

static_assert(std::is_base_of<Component, T>::value,
              "Event handler method must belong to caller class");

Aucun commentaire:

Enregistrer un commentaire