c++templatesc++17variadic-templates

How to write a template with a default policy and variadic parameters


I am experimenting with writing a lightweight signals/observer class that has the following interface (implementation removed for simplification):

template <typename ... ARGS>
class Signal
{
public:
    void connect(AbstractSlot<ARGS...>& slot);
    void disconnect(AbstractSlot<ARGS...>& slot);
    void emit(const ARGS&...args);

    //...
};

Which is used, for example, like so:

Signal<> signal1; //A signal with no parameters
Signal<int, int> signal2; //A signal with two int parameters

signal1.emit();
signal2.emit(10, 20);

I want to be able to make my class optionally thread-safe. My thought was to modify my template to accept a "locking policy" class that, by default, is "empty"

struct NoLock
{
    void lock() {}
    void unlock() {}
};

In my head, I'd like to be able to use the modified Signal template like so:

Signal<> signal1;
Signal<std::mutex> signal1Locked;
Signal<int, int> signal2;
Signal<int, int, std::mutex> signal2Locked;

Which would require me to write the template like so:

template <typename ... ARGS, typename LockPolicy = NoLock>
class Signal;

Except that this isn't valid C++ (and I understand why; how would the compiler know the difference between the policy and the variadic arguments).

My question is, how can I achieve what I desire in a syntax that isn't too confusing/complex to use?


Solution

  • How about

    template <typename Signature, typename LockPolicy = NoLock> class Signal;
    
    template <typename... Args, typename LockPolicy>
    class Signal<void(Args...), LockPolicy>
    {
    // ...
    };
    
    
    Signal<void()> signal1;
    Signal<void(int, int), std::mutex> signal2;
    

    or use alias (and change order):

    template <typename LockPolicy , typename... Args> class LockSignal
    {
    // ...
    };
    
    template <typename... Args>
    using Signal = LockSignal<NoLock, Args...>;
    
    Signal<> signal1;
    LockSignal<std::mutex, int, int> signal2;