tags:

views:

332

answers:

5

I have this weird issue. Think of your basic openGL programs. They simply run from a main method and contain callbacks like `glutMouseFunc(MouseButton) where MouseButton is the name of a method.

What I have done is I have encapsulated the main file into a class, so that MouseButton is no longer a static function but has an instance. But doing this gives me a compilation error :

Error 2 error C3867: 'StartHand::MouseButton': function call missing argument list; use '&StartHand::MouseButton' to create a pointer to member c:\users\angeleyes\documents\visual studio 2008\projects\capstone ver 4\starthand.cpp 388 IK Engine

It is not possible to provide a code sample as the class is quite huge.

I have tried using this->MouseButton but that gives the same error. Can't a pointer to an instance function be given for callback?

+2  A: 
Roger Pate
You should not use static functions as callbacks. Technically the library is expecting a C-Function. Static member methods are not C-Functions and you are just getting lucky that your compiler uses the same ABI for both. There is no requirement for it to do so. And just to grind it in, the more expensive compilers that use highly optimized ABI's (and pass parameters in registers rather than on the stack) will break with the following code.
Martin York
Actually this example is standard C++ and guaranteed to work in C++. The problem is language and compiler interop, and your point is valid in those circumstances. OpenGL is in exactly that situation. Fully answering how to specify compiler-/platform-specific ABIs when declaring the function is outside the scope of this question, however.
Roger Pate
I posted a question (link to follow) so that you can discuss the ABI matter more thoroughly. I'm really interested in this, because I had often used static methods to do Roger's "trampoline" trick (hehe I like that term, Roger).http://stackoverflow.com/questions/2068022/in-c-is-it-safe-portable-to-use-static-member-function-pointer-for-c-api-ballb
Emile Cormier
I didn't name it. It means any function whose purpose is delegating to another function, and in some cases means a specific assembly construct which is optimized for exactly that.
Roger Pate
Martin: I believe you that it causes problems in some compilers, but now I'm curious which ones. Care to share?
Roger Pate
A: 

You can't replace a static callback with an instance one. When the caller calls your callback, on what instance whoul it call? In other words, how does the caller pass in the formal 'this' argument?

The solution is to have a static callback stub and pass the instance as argument, which implies the callee must accept an arbitrary pvoid that will pass back when invoking the callback. In the stub, you can then call the non-static method:

class C {
    void f() {...}
    static void F(void* p) {
      C* pC = (C*)p;
      pC->f();
    }
  }

  C* pC = ...;
  someComponent.setCallback(&C::F, pC);
Remus Rusanu
+1  A: 

No, a pointer to an instance function can not be given to a callback function expecting a function pointer of a certain signature. Their signatures are different. It won't compile.

Generally such APIs allow you to pass in a void* as a "context" parameter. You pass in your object there, and write a wrapper function which takes the context as the callback. The wrapper casts it back to whatever class you were using, and calls the appropriate member function.

Terry Mahaffey
+1  A: 

You cannot use a non-static member function in this case. Basically the type of the argument expected by glutMouseFunc is

void (*)(int, int, int, int)

while the type of your non-static member function is

void (StartHand::*)(int, int, int, int)

First problem is that types don't really match. Second, in order to be able to call that method, the callback would have to know which object ( i.e. "this" pointer ) your method belongs to ( that's pretty much why the types are different in the first place ). And third, I think you're using the wrong syntax to retrieve the method's pointer. The right syntax should be: &StartHand::MouseButton.

So, you have to either make that method static or use some other static method that would know which StartHand pointer to use to call MouseButton.

Iustin Amihaesei
A: 

Contrary to what everyone seems to be saying, you most definitely CAN use a non-static member function as a callback method. It requires special syntax designed specifically for getting pointers to non-static members, and special syntax to call that function on a specific instance of a class. See here for a discussion of the needed syntax.

Here is sample code that illustrates how this works:

#include <cstdlib>
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;


class Operational
{
public:
    Operational(int value) : value_(value) {};

    string FormatValue() const ;

private:
    int value_;

};

string Operational::FormatValue() const
{
    stringstream ss;
    ss << "My value is " << value_;
    return ss.str();
}

typedef string(Operational::*FormatFn)() const; // note the funky syntax

Operational make_oper(int val)
{
    return Operational(val);
}

int main()
{
    // build the list of objects with the instance callbacks we want to call
    Operational ops[] = {1, 2, 3, 5, 8, 13};
    size_t numOps = sizeof(ops)/sizeof(ops[0]);

    // now call the instance callbacks
    for( size_t i = 0; i < numOps; ++i )
    {
        // get the function pointer
        FormatFn fn = &Operational::FormatValue;    

        // get a pointer to the instance
        Operational* op = &ops[i];  

        // call the callback on the instance
        string retval = (op->*fn)();

        // display the output
        cout << "The object @ " << hex << (void*)op << " said: '" << retval << "'" << endl;
    }


    return 0;
}

The output of this program when I ran it on my machine was:

The object @ 0017F938 said: 'My value is 1' 
The object @ 0017F93C said: 'My value is 2' 
The object @ 0017F940 said: 'My value is 3' 
The object @ 0017F944 said: 'My value is 5' 
The object @ 0017F948 said: 'My value is 8' 
The object @ 0017F94C said: 'My value is 13'
John Dibling
Presumably the OP doesn't have control over the library to modify it to use ptmfs, so if it doesn't already support them, then they cannot be used. As I have the oldest answer and already state this, I'm not sure what you mean by "contrary to what everyone seems to be saying".
Roger Pate
@Roger Pate: Someone is misunderstood - either me, or the OP, or the other respondants. My understanding was that OP was asking "Can member functions be used as callback functions" and everyone else said "No." I may very well have misunderstood both the OP and the respondants.
John Dibling
@Roger - Your example shows binding using static function. So I don't think your answer is correct. You need to use ::* to get ptr to method of any object instance.
Ketan
Ketan: What do you mean? ::* is not an operator. How is my answer incorrect?
Roger Pate
John: That's what I was trying to comment originally, I never said "no", but "you can use a ptmf directly, if the api which you're using supports that, but since it's most likely that api doesn't support them, here's <example of alternative>..."
Roger Pate