0

I'm trying to run a method of one class from inside another class, I have a basic GUI that lets the user fill his name and password, and when it clicks the login button another takes over and handles the event, I would like to set a callback in case of a successful login, but I'm having problems sending the function pointer to my handler.

I made a raw example of what I'm trying to do.

#include <iostream>

using namespace std;

class LoginHandler {
public:
    void loginAttempt(bool result)
    {
        if(result == true)
        {
            cout << "Login success! W00t!" << endl;

            //Run my callback :)
            callback();
        } else {
            cout << "Login failed." << endl;
        }
    }

    void setCallback(void (*cb)())
    {
        callback = cb;
    }

private:
    void (*callback)();
};

class Foo {
public:
    void run()
    {
        LoginHandler* loginHandler = new LoginHandler();
        loginHandler->setCallback(&Foo::sayHello);

        loginHandler->loginAttempt(false);
        loginHandler->loginAttempt(true);
    }

    void sayHello()
    {
        cout << "You actually logged in! Isn't that amazing!?" << endl;
    }
};

int main()
{
    Foo foo;
    foo.run();

    return 0;
}

I'm getting the next error:

In member function 'void Foo::run()':
error: no matching function for call to 'LoginHandler::setCallback(void (Foo::*)())'
note: candidate is: void LoginHandler::setCallback(void (*)())
note:   no known conversion for argument 1 from 'void (Foo::*)()' to 'void (*)()'

Thanks in advice.

4

3 回答 3

1

You've specified that "setCallback" takes a function pointer, not a member-function pointer.

void setCallback(void (*cb)())

you are calling it - as the compiler complains - with a member-function pointer

loginHandler->setCallback(&Foo::sayHello);

In order to take a member function pointer, the setCallback definition is going to have to be able to determine what class to use, and the location where you call the callback is going to have to know what class so it can dereference the pointer.

Generally people solve this with a C-style callback handler, either

static FooSayHelloCallback(void* fooParam)
{
    Foo* foo = static_cast<Foo*>(fooParam);
    foo->sayHello();
}

or a functor object

struct LoginHandlerCallbackFunctor
{
    void* m_this;
public:
    FooSayHelloFunctor(void* this_) : m_this(this_) {}
    virtual void operator()() = 0;
};

struct FooSayHelloFunctor : public LoginHandlerCallbackFunctor
{
public:
    FooSayHelloFunctor(Foo* foo_) : LoginHandlerCallbackFunctor(foo_) {}
    virtual void operator()() { ((Foo*)m_this)->sayHello(); }
};

Then

class LoginHandler {
    ...
    LoginHandlerCallbackFunctor m_callback;
    ...

    void setCallback(const LoginHandlerCallbackFunctor& functor)
    {
        m_callback = functor;
    }

    void doCallback()
    {
        m_callback(); // calls operator()
    }

If you want to get really fancy you could maybe do that with templates.

Or, alternatively, you could use C++11 Lambdas and std::function.

class LoginHandler
{
  typedef std::function<void(void)> GreetingCallback;

  GreetingCallback m_callback;
  ...
  void setCallback(const GreetingCallback& callback_)
  {
      m_callback = callback_;
  }

  void doCallback()
  {
      m_callback();
  }
}

and in foo

LoginHandler loginHandler();
loginHandler.setCallback([=]() { sayHello(); });

loginHandler->loginAttempt(false);
loginHandler->loginAttempt(true);
于 2013-06-20T05:59:53.197 回答
1

Raw function pointers are mostly obsolete and should be used only for low-level performance-critical stuff. For the rest of us C++ has std::function which is infinitely better. It handles cases like yours with ease.

If you cannot use a it because you don't have a C++11 compiler, boost::function is a drop-in substitute.

于 2013-06-20T06:22:04.247 回答
0
  1. sayHello should be a static function. Because ordinary member functions have implicit parameter ¨this¨. So the full signature of sayHello is void sayHello(Foo* this);

  2. Another way - use pointer to class member.

  3. Also you can use functor structure instead of function.

于 2013-06-20T02:06:29.220 回答