Search code examples
c++templatesc++14variadic-templatesnon-type-template-parameter

template non-type template parameters


Following is my code to register a free function or member function as callback.

Find code here https://cppinsights.io/s/58dcf235

#include <stdio.h>
#include <iostream>
#include <functional>
#include <vector>
using namespace std;

class IEvent
{
    
public:
    int m_EventType;
    virtual ~IEvent() {}
};

template<class...Args>
class Event : public IEvent {};

template<int eventType,class...Args>
class Event<int eventType, bool(Args...)> : public IEvent
{
public:
    Event(bool(*func)(Args...)) :m_FnPtr(func)
    { 
         m_EventType = eventType;
         m_ListenersList.push_back(m_FnPtr);
    }

    template <typename T>
    Event(T* obj, bool(T::* Func)(Args...))
    {
        m_EventType = eventType;
        m_FnPtr = [obj, Func](Args&&... args)->bool {
            return (obj->*Func)(std::forward<Args>(args)...);
        };
        
        m_ListenersList.push_back(m_FnPtr);
    }

    void NotifyListeners(Args&&...args) const
    {
        for (auto& itr : m_ListenersList)
        {
            (itr)(std::forward<Args>(args)...);
        }
        
    }
private:
    std::function<bool(Args...)> m_FnPtr;
    std::vector<std::function<bool(Args...)>> m_ListenersList; 
};

class Window
{
public:
    bool OnKeyUp(bool, double)
    {
        cout << endl << "Member Function called";
        return true;
    }

    bool OnClicked()
    {
        cout << endl << "OnClicked";
        return true;
    }
    
    //using KeyupListenerType = Event<"KeyUp", bool(bool, double)>;
};

int main()
{
    Window w;
    Event<90,bool(bool, double)> evnt(&w, &Window::OnKeyUp);
    //Event<100,bool()> evnt(&w, &Window::OnClicked);
    evnt.NotifyListeners(true, 6.8);
    return 0;
}

But I'm getting errors at line:

Event<90,bool(bool, double)> evnt(&w, &Window::OnKeyUp);

I'm trying to assign an event type to the event listener as shown below. I want to assign the event type during instantiation itself. But I get the following errors

26 | class Event<int, bool(Args...)> : public IEvent
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:26:7: note:         ‘eventType’
main.cpp: In function ‘int main()’:
main.cpp:80:32: error: type/value mismatch at argument 1 in template parameter list for ‘template class Event’
   80 |     Event<90,bool(bool, double)> evnt(&w, &Window::OnKeyUp);
      |                                ^
main.cpp:80:32: note:   expected a type, got ‘90’
main.cpp:80:62: error: expression list treated as compound expression in initializer [-fpermissive]
   80 |     Event<90,bool(bool, double)> evnt(&w, &Window::OnKeyUp);
      |                                                              ^
main.cpp:80:62: error: cannot convert ‘bool (Window::*)(bool, double)’ to ‘int’ in initialization
main.cpp:81:10: error: request for member ‘NotifyListeners’ in ‘evnt’, which is of non-class type ‘int’
   81 |     evnt.NotifyListeners(true, 6.8);
      |          ^~~~~~~~~~~~~~~

What mistake I'm doing? How to pass non-type parameters?


Solution

  • Your base declaration does not match your specialization.

    The base implementation has template <class...Args> while the specialzation wants template <int eventType, class...Args>.

    You also put an extra int that does not belong there in the declaration for the specialization here:

    template<int eventType,class...Args>
    class Event<int eventType, bool(Args...)> : public IEvent
                ^^^ here
    

    The adjusted code would look like this

    #include <stdio.h>
    #include <iostream>
    #include <functional>
    #include <vector>
    using namespace std;
    
    class IEvent
    {
        
    public:
        int m_EventType;
        virtual ~IEvent() {}
    };
    
    template<int eventType, class...Args>
    class Event : public IEvent {};
    
    template<int eventType,class...Args>
    class Event<eventType, bool(Args...)> : public IEvent
    {
    public:
        Event(bool(*func)(Args...)) :m_FnPtr(func)
        { 
             m_EventType = eventType;
             m_ListenersList.push_back(m_FnPtr);
        }
    
        template <typename T>
        Event(T* obj, bool(T::* Func)(Args...))
        {
            m_EventType = eventType;
            m_FnPtr = [obj, Func](Args&&... args)->bool {
                return (obj->*Func)(std::forward<Args>(args)...);
            };
            
            m_ListenersList.push_back(m_FnPtr);
        }
    
        void NotifyListeners(Args&&...args) const
        {
            for (auto& itr : m_ListenersList)
            {
                (itr)(std::forward<Args>(args)...);
            }
            
        }
    private:
        std::function<bool(Args...)> m_FnPtr;
        std::vector<std::function<bool(Args...)>> m_ListenersList; 
    };
    
    class Window
    {
    public:
        bool OnKeyUp(bool, double)
        {
            cout << endl << "Member Function called";
            return true;
        }
    
        bool OnClicked()
        {
            cout << endl << "OnClicked";
            return true;
        }
        
        //using KeyupListenerType = Event<"KeyUp", bool(bool, double)>;
    };
    
    int main()
    {
        Window w;
        Event<90,bool(bool, double)> evnt(&w, &Window::OnKeyUp);
        //Event<100,bool()> evnt(&w, &Window::OnClicked);
        evnt.NotifyListeners(true, 6.8);
        return 0;
    }