Question

I have a function

void newEvent(void (*onRun)(), std::string eventName)

which I call like so

newEvent((*Event1)(), "Event1");

where Event1() is obviously a void function. This is my build output:

/home/mrasicci/Programming/SSCE/main.cpp||In member function ‘void MenuState::enter()’:|
/home/mrasicci/Programming/SSCE/main.cpp|45|error: invalid use of member function (did you forget the ‘()’ ?)|
||=== Build finished: 1 errors, 0 warnings ===|

I have tried changing the newEvent((*Event1)(), "Event1"); to other things such as newEvent((*Event1), "Event1"); or newEvent(Event1(), "Event1"); or newEvent(Event1, "Event1"); none of which worked, all of which gave different errors. Here's the example code which gives the error, the expected output would be "Event1 triggered" but it doesn't compile:

#include <iostream>
#include <vector>

namespace TGE
{
    class EventListener
    {
        public:
            typedef struct
            {
                void (*run)();
                bool triggered;
                std::string name;
            } Event;

            void newEvent(void (*onRun)(), std::string eventName)
            {
                Event newEvent;
                newEvent.name = eventName;
                newEvent.triggered = false;
                newEvent.run = onRun;
                eventStack.push_back(newEvent);
            }

            void trigger(std::string eventName)
            {
                std::vector<Event>::iterator itr;

                for(itr = eventStack.begin(); itr != eventStack.end(); itr++)
                {
                    if(itr->name == eventName) itr->triggered = true;
                }
            }

        protected:
            std::vector<Event> eventStack;
    };
}

class MenuState : protected TGE::EventListener
{
    public:
        void enter()
        {
            newEvent((*Event1)(), "Event1");
            trigger("Event1");

            std::vector<Event>::iterator itr;
            for(itr = eventStack.begin(); itr != eventStack.end(); itr++)
            {
                if(itr->triggered)
                {
                    itr->run();
                    itr->triggered = false;
                }
            }
        }

        void Event1()
        {
            std::cout << "Event1";
        }
};

int main()
{
    MenuState* menuState = 0;
    menuState = new MenuState();
    menuState->enter();

    return 0;
}
Was it helpful?

Solution

Writing the code like this newEvent((*Event1)(), "Event1"); is wrong because you are dereferencing a function name.

Writing it as newEvent(Event1(), "Event1"); would mean that you are trying to use the return value of a void function as a function pointer.

Saying newEvent(Event1, "Event1"); is only a little better, since you'll be trying to pass a "pointer to member function" as a "pointer to function". Since a member function needs an instance to run, this won't work either.

I don't know what your larger architecture looks like, but a more general solution would be to use std::function instead of simple function pointers:

struct Event
{
    std::function<void()> run; // requires <functional>
    bool triggered;
    std::string name;
};

Now, you can pass in ordinary function pointers, pointer-to-members, lambdas, functors, etc. as your callback.

Here's one way to change the rest of the code to make it work (note: it is not the only way; but it's the easiest way IMO to have generality and flexible design. Full source code here.)

Change the first line of newEvent to this:

template <typename F>
void newEvent(F onRun, std::string eventName)
{...}

And call it like this:

newEvent([=](){Event1();}, "Event1");
//       ^^^^^^^^^^^^^^^^
//       This is a lambda

These are C++11 features. If you are unfamiliar or uncomfortable or unable to use C++11 features for some reason, I'd suggest using the old virtual base class interface way, with a specific pure virtual method as a mouse even callback, and passing around pointers to objects instead of pointers to member functions.

You can pass around and later call pointer to member functions, but you'd need a pointer to an instance of that specific class to call it on too. I think the design will quickly get complicated and is not worth it, but you can explore that route if you are so inclined.

OTHER TIPS

Event1 is a member function, so you need to qualify it as:

void newEvent(void (EventListener::*onRun)(), std::string eventName)
//                  ^^^^^^^^^^^^^^^

And you call it as:

newEvent(&EventListener::Event1, "Event1");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top