dimanche 30 avril 2017

Is there a way to partially match a variadic template parameter pack?

I currently have a system to "connect" signals to functions. This signal is a variadic template that has as template parameters the arguments of the functions if can connect to.

In the current implementation, I obviously cannot connect to functions whose arguments aren't exactly the same (or those that can be converted to) as the signal's parameters. Now, as I'm trying to mimic Qt's signal/slot/connect, I'd also like to connect a signal of N parameters to a slot of M<N parameters, which is perfectly well-defined (i.e. ignore the >M parameters of the signal and just pass the first M to the connected function). For an example of the code I have in its most simplistic form, see Coliru.

So the question is two-fold:

  1. How do I make the connect call work for a function void g(int);?
  2. How do I make the emit call work for a function void g(int);?

I'm guessing I'll have to make some "magic" parameter pack reducer for both the slot and the , but I can't see how it all should fit together so it's quite hard to actually start trying to code a solution. I'm OK with a C++17-only solution, if at least Clang/GCC and Visual Studio 2017 can compile it.

The code linked above for completeness:

#include <memory>
#include <vector>

template<typename... ArgTypes>
struct slot
{
    virtual ~slot() = default;

    virtual void call(ArgTypes...) const = 0;
};

template<typename Callable, typename... ArgTypes>
struct callable_slot : slot<ArgTypes...>
{
    callable_slot(Callable callable) : callable(callable) {}

    void call(ArgTypes... args) const override { callable(args...); }

    Callable callable;
};

template<typename... ArgTypes>
struct signal
{
    template<typename Callable>
    void connect(Callable callable)
    {
        slots.emplace_back(std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
    }

    void emit(ArgTypes... args)
    {
        for(const auto& slot : slots)
        {
            slot->call(args...);
        }
    }

    std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
};

void f(int, char) {}

int main()
{
    signal<int, char> s;
    s.connect(&f);

    s.emit(42, 'c');
}

Aucun commentaire:

Enregistrer un commentaire