0

I'm trying to implement an event manager based on the linked code in the top answer here: Game Objects Talking To Each Other

However I'm getting an error when I try to register the callbacks. I'm sure it has to do with the typedef, and I admit I'm not sure how it works exactly, but it is in the exact same form in the linked code. The B class should be inherriting from the Interface, so why is the type different? I've condensed the code into the smallest example below.

#include <iostream>

class Interface;
typedef void (Interface::*Callback)(void *data);

class Interface
{
    public:
        void Register    (Callback func);

};

void Interface::Register(Callback func)
{
    std::cout << "Register" << std::endl;
}


class B : public Interface
{
    public:
        B();
        void Echo(void *data);
};

B::B()
{
    Register( (Callback)Echo );
}

void B::Echo(void *data)
{
    std::cout << "Echo" << std::endl;
}


int main()
{
    B b;
    return 0;
}

Here's the error I get under g++ 4.6.1:

test.cpp: In constructor ‘B::B()’:
test.cpp:31:22: error: argument of type ‘void (B::)(void*)’ does not match ‘Callback {aka void (Interface::*)(void*)}’

Could anyone please explain what I'm doing wrong? Thanks

Community
  • 1
  • 1
Shootfast
  • 1,918
  • 2
  • 17
  • 27

2 Answers2

1

As @Kerrek correctly pointed out, Echo is not a member of Interface, therefore B::Echo doesn't qualify as Interface::*Callback. But you can use a template to accomplish that, e.g.:

template <class T> class Interface {
public:
    typedef void (T::*Callback)(void *data);
    void Register(Callback func) {
        std::cout << "Register" << std::endl;
    }
    // ...
};

class B : public Interface<B> {
public:
    B() {
        Register(&B::Echo);
    }
    void Echo(void *data) {
        // Do something
    }
};
Community
  • 1
  • 1
jweyrich
  • 31,198
  • 5
  • 66
  • 97
  • I think using the template will be the best solution. I'm not sure how the code I linked to works then, perhaps it doesn't :P – Shootfast Dec 13 '11 at 23:19
0

I think you might be better off using std::function (c++11) or boost::function (c++03+boost)

#include <iostream>

class Interface;
typedef void (Interface::*Callback)(void *data);

class Interface
{
    public:
        std::function<void(void*)> register;

            Interface(std::function<void(void*)> register_)
            :    register(register_)   //intializer list
            {}
            virtual ~Interface(){} //put me in
};

void Interface::Register(Callback func)
{
    std::cout << "Register" << std::endl;
}


class B : public Interface
{
    public:
        B();
        void Echo(void *data);
};

B::B()
:   Interface( std::bind(B::Echo, this) )
{}

void B::Echo(void *data)
{
    std::cout << "Echo" << std::endl;
}

Although why you aren't using pure virtuals is beyond me

class Interface
{
    public:
        virtual void Echo(void*)=0;

};

void B::Echo(void *data) //implements Echo
{
    std::cout << "Echo" << std::endl;
}

call interface->echo will call the child

if you need performance then use the

http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

And be very careful with void* they are generally considered bad.

EDIT ADDRESSING POINT IN COMMENTS: non pure virtuals

class Interface
{
public:
    virtual ~Interface(){} //put me in
    virtual void echo(void*){}  //if implementation is not extended it will do nothing.
    //others 
};

This ins't Java, interfaces aren't a thing defined by the language. This way you can have an interface which you can pick can choose which part to implement, if a callback doesn't concern your class, then just don't implement it.

void* are bad for a whole host of reasons. from C++ FAQ

avoid void* (keep them inside low-level functions and data structures if you really need them and present type safe interfaces, usually templates, to your users)

http://www2.research.att.com/~bs/bs_faq.html

search on "void*"

but basically void* bypass all the type safety that C++ went out of it's way adding. It is a hack in C to make up for the fact that it doesn't have any polymorphism or generic code.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
111111
  • 15,686
  • 6
  • 47
  • 62
  • I can't use virtuals, because then every callback for every inherited class would need to be implemented in the Interface class. What's dangerous about using void* ? – Shootfast Dec 13 '11 at 23:21
  • Read what I have appended to the bottom of the reply. – 111111 Dec 13 '11 at 23:47
  • @Shootfast: Avoiding `void*` is a good advice. The main reason is that a `void *` can be implicitly converted to any pointer type, which is not safe, and might gift you with some debugging hours. – jweyrich Dec 14 '11 at 01:20