13 Replies - 3484 Views - Last Post: 17 June 2011 - 10:52 PM Rate Topic: -----

#1 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Callback functions and classes

Posted 03 June 2011 - 04:11 AM

I've been learning about OpenGL in an attempt to finally get writing C++ games (2d for now) and I've created a wrapper class. However, GLUT (an extension to OpenGL) uses callback functions in a manner similar to WinAPI and I've realised that I need to give it a pointer to the class member function. Is it possible to do this for a specific object of a class, or must I instead create a global object and create callback functions that call the global object's member functions? Any attempt to simply do:
glutDisplayFunc(&(handler.render_scene));

results in the following Visual C++ 2008 error:
main.cpp(109) : error C2276: '&' : illegal operation on bound member function ex pression


Any tips?

Edit:

I've worked out the following:
class test;
test *g_callback;
class test
{
	public:
		static int s_add (int a, int B)/> { return g_callback->add(); }
		int add (void) { return this->a + this->b; }//sample class based callback function

		int a;
		int b;
};
int add (int a, int B)/>
//sample non-class based callback function
{
	return a + b;
}
//cannot modify -- sample saving callback
int (*callback)(int, int);
//cannot modify -- sample registering callback
void register_callback (int (*f)(int, int))
{
	callback = f;
}
int callback_add (int a, int B)/>
//cannot modify -- sample calling callback
{
	return (*callback)(a, B)/>;
}

int main ()
{
	test x;
	x.a = 1;
	x.b = 2;
	g_callback = &x;
	register_callback(&(test::s_add));
	//cannot modify -- sample using callback
	return callback_add(5, 8);
}
However this still requires a global pointer. Is there any way to avoid the use of global variables completely? Remember I cannot modify any code calling the callback function.

This post has been edited by PlasticineGuy: 03 June 2011 - 04:41 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Callback functions and classes

#2 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2246
  • View blog
  • Posts: 9,236
  • Joined: 18-February 07

Re: Callback functions and classes

Posted 03 June 2011 - 07:12 AM

Generally speaking pointers to member functions, although possible, don't tend to work the way you might wish them to. Basically if a have a non-static member function
int MyClass::func(int arg)
the actual signature for the function will look something like:

int MyClass_func(MyClass* this, int arg)*

so in order to call func on an instance of MyClass you need to pass in the pointer to the particular instance -- this generally does not work well with callback function which generally have a prescribed syntax that does not include the pointer to some unknown class that some programmer may one day decided to write. So... generally for the purpose of callback functions you have to use either a vanilla function or a static member function. Static member functions do not require the pointer to a particular instance so they can be used.

(reading material from C++FAQ)

*note that there is no prescribed standard way that a member function has to be defined in the implementation, so the signature I posted above does not HAVE to be how your compiler actually handles member functions -- so that is another layer to the problem.
Was This Post Helpful? 1
  • +
  • -

#3 Xupicor  Icon User is offline

  • Nasal Demon
  • member icon

Reputation: 249
  • View blog
  • Posts: 582
  • Joined: 31-May 11

Re: Callback functions and classes

Posted 03 June 2011 - 07:17 AM

I'm not really into OpenGL that much (meaning - I never had to use it, although I'll probably meet the problems you battle now soon enouch, since I'm putting together a small "game" myself, in my own free time), but I think there's no way around it. Sure, you can make it into a static member variable, but it wouldn't change the situation much, right? ;)

OpenGL isn't really that much geared towards OOP, since it's written in C.


But like I said, game programming and OpenGL are not really my deal. You're probably get some better answer from someone who already had to confront the problem.

This post has been edited by Xupicor: 03 June 2011 - 07:25 AM

Was This Post Helpful? 0
  • +
  • -

#4 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Re: Callback functions and classes

Posted 03 June 2011 - 07:19 AM

Thanks Nick,
I understand the static member functions do not require a pointer to a certain instance in order to function. However, I want to be able to access the member variables of this certain instance from the callback function. There are a couple of quick and dirty workarounds I have already posted, but I was hoping to find a cleaner and more legible solution. Sadly there seems to be no way.

You note pointers to member functions are possible (though might not work the way one may wish). Could you please elaborate on potential uses?

To Xupicor -- thanks for responding. I chose OpenGL over DirectX because I have already tried my hand with DirectX and my experiences have been, shall we say, less than pleasurable. I originally set out to experiment with John Conway's Game of Life and other cellular automata, but I am aiming towards 2D graphics and games of the sort I can already do in simplistic programs such as Game Maker.

The reason I've decided to use C++ recently is because of the performance issues inherent in interpreted languages. The lack of raw computational power is a severe drawback and one that I am sick of.

This post has been edited by PlasticineGuy: 03 June 2011 - 07:23 AM

Was This Post Helpful? 0
  • +
  • -

#5 Xupicor  Icon User is offline

  • Nasal Demon
  • member icon

Reputation: 249
  • View blog
  • Posts: 582
  • Joined: 31-May 11

Re: Callback functions and classes

Posted 03 June 2011 - 07:23 AM

Read about pointers to members here: http://www.parashift...to-members.html
Was This Post Helpful? 1
  • +
  • -

#6 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Re: Callback functions and classes

Posted 03 June 2011 - 07:24 AM

Oh, right; I forgot about Nick's link.
Was This Post Helpful? 0
  • +
  • -

#7 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2246
  • View blog
  • Posts: 9,236
  • Joined: 18-February 07

Re: Callback functions and classes

Posted 03 June 2011 - 08:24 AM

Ok so here is the problem -- the callback function has a percsribed syntax that does not include any kind of identifier (i.e. I can't pass in a number or pointer and have that returned to the callback function).

So lets say I have a function:

AjaxDownload( char* url, int(*success)(char*) )

and upon successful download this function will call the success callback function.

I have two object ObjectA and ObjectB who I want to utilize this function via their individual AjaxObj::handler(char*) functions.

What todo? Well the one time I did this in the past was to create a proxy class that maintained a "pool" of static functions and handed out pointers to them upon request. Lets call this class CallBackProxy.

CallBackProxy has all static members: it has a pool of generic functions:

static int handler1(char *);
static int handler2(char *);
static int handler3(char *);
etc..

it has a static map<int (*)(char*), AjaxObj*> (or some other associative structure) and then it uses a semaphore pattern to hand out function pointers.

the static functions actually look something like this inside:

static int handler1(char * arg) {
    return (objectMap[handler1]) ? objectMap[handler1]->handler(arg) : 0;
}


This has it problems: chiefly that you have only a static number of available callback slots (handler function inside of CallBackProxy). Since CallBackProxy uses a semaphore patten technically all you have to do is wait until another handler becomes available -- however now you are getting into multi-threading types of situations and it is possible to generate a deadlock where one of the handler's can't be released until one is free.

So can we do better? Can we have a dynamic pool of handler functions? Yes, actually we can, but we will have to dive a little into the arena of assembly language programming (well not necessarily but the way I envision). Because we have to dynamically create copies of the function and customize them as we do... fun stuff! but not exactly strictly C++ paradigm-centric.
Was This Post Helpful? 1
  • +
  • -

#8 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Re: Callback functions and classes

Posted 03 June 2011 - 08:28 AM

Thank your for your in depth and detailed reply.
I think I understand what you're getting at. However, I think that in this case a global pointer is a necessary evil to avoid overcomplication that could then introduce further subtle bugs.
Inline assembly is not something I want to resort to as I've never used it before and (I think) it's very compiler-specific.
Was This Post Helpful? 0
  • +
  • -

#9 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2246
  • View blog
  • Posts: 9,236
  • Joined: 18-February 07

Re: Callback functions and classes

Posted 03 June 2011 - 09:40 AM

This might be an interesting read for you: CALLBACKS IN C++ USING TEMPLATE FUNCTORS
Was This Post Helpful? 1
  • +
  • -

#10 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2246
  • View blog
  • Posts: 9,236
  • Joined: 18-February 07

Re: Callback functions and classes

Posted 03 June 2011 - 01:46 PM

So here is a little proof-of-concepts program that uses a manager to manage a pool of 2 functions that can be used to wrap calls to objects. Now I have the added abstraction of function pointers here which probably is not necessary, and I created a template function for the actual call back functions which is also not necessary.

#include <iostream>
#include <map>

using namespace std;

//----------- BEGIN CALLMAN.HPP ----------------------

template<typename FUNCTION_PTR, typename FUNCTOR>
class CallBackManager {
    public:
    typedef FUNCTION_PTR function_type;
    typedef FUNCTOR functor_type;
    typedef map<FUNCTION_PTR, FUNCTOR*> map_type;
    typedef pair<FUNCTION_PTR, FUNCTOR*> pair_type;
    
    private:

    static map_type functions;
    static int count;
    static functor_type* NULL_FUNCTOR;
    //ensure no one tries to instanciate
    CallBackManager() { }; 
    
    static typename map_type::iterator firstEmpty() {
        for(typename map_type::iterator it = functions.begin(); it != functions.end(); ++it) {
            if (it->second == NULL_FUNCTOR) {
                return it;
            }
        }
        return functions.end();
    }
    
    public:

    static bool insert(FUNCTION_PTR func) {
        pair<typename map_type::iterator, bool> retValue;
        retValue = functions.insert( pair_type(func, NULL_FUNCTOR ));
        //cout <<"attempt to insert: " << func << " - " << retValue.second << endl;
        if (retValue.second) {
            count++;
        }
        return retValue.second;
    }
    
    static function_type allocPointer(functor_type& functor) {
        //cout << "Alloc: count = " << count << endl;
        if (count > 0) {
            count--;
            typename map_type::iterator freeSlot = firstEmpty();
            if (freeSlot != functions.end()) {
                freeSlot->second = &functor;
                cout << "Alloc: set function ptr" << endl;
                return freeSlot->first;
            } else {
                return NULL;
            }
        }
        return NULL;    
    }
    
    static bool freePointer(function_type fn) {
        typename map_type::iterator item = functions.find(fn);
        if (item != functions.end()) {
            item->second = NULL_FUNCTOR;
            count++;
            return true;
        }
        return false;    
    }

    static functor_type* get(function_type fn) {
        return functions[fn];
    }
};

template<typename FUNCTION_PTR, typename FUNCTOR>
typename CallBackManager<FUNCTION_PTR, FUNCTOR>::map_type CallBackManager<FUNCTION_PTR, FUNCTOR>::functions;

template<typename FUNCTION_PTR, typename FUNCTOR>
typename CallBackManager<FUNCTION_PTR, FUNCTOR>::functor_type* CallBackManager<FUNCTION_PTR, FUNCTOR>::NULL_FUNCTOR = (functor_type*)NULL;

template<typename FUNCTION_PTR, typename FUNCTOR>
int CallBackManager<FUNCTION_PTR, FUNCTOR>::count = 0;

//------------ END CALLMAN.HPP



// We will use the Foo class to test with, we will pass the message() function as a callback function
struct Foo {
    static int counter;
    int index;
    int parentIndex;
   
    Foo() : index(++counter), parentIndex(0) {
        cout << *this << " created." << endl;
    }
    
    Foo(const Foo& obj) : index(++counter), parentIndex(obj.index) {
        cout << *this <<  " created from " << obj << "." << endl;
    }
    
    Foo& operator=(const Foo& rhs) {
        if(&rhs != this) {
            cout << *this << " becomes ";
            parentIndex = rhs.index;
            cout << *this <<  " via assignment from " << rhs << "." << endl;
        }
        return *this;
    }
    
    ~Foo() { cout << *this << " retired." << endl; }
    
    int message(char* text); 

    //not really required since Foo has no private members but simplifies forward declarations.
    friend ostream& operator<<(ostream& out, const Foo& rhs);

};

int Foo::counter = 0;

ostream& operator<<(ostream& out, const Foo& rhs) {
    return out << "Foo{" << rhs.index << ":" << rhs.parentIndex << "}";
}

int Foo::message(char* text) {
    cout << *this << " " << text << endl; 
    return index;
}




//Make the function pointer easier if we define it as a typedef
typedef int (*CallBackFn)(char*);

// Now we need a function that takes a callback:
void IneedCallback( CallBackFn callback) {
    char messageText[] = "is dealing with a callback funtion at this time!";
    int calledIndex = (*callback)(messageText);
    cout << "IneedCallback did work for: " << calledIndex << endl;
}


// Now HOW to use the CallBackManager class:

//Step #1 Create a functor (we could technically skip using functors but they kind of help generalize)
struct FooMessageCall {
    Foo* foo;
    FooMessageCall(Foo& obj) : foo(&obj) { }
    int operator()(char* text) {
        return foo->message(text);
    }
};

//Step #2: we need a callBackFunction -- I choose to use a template function so that they would be
//  easy to generate withouth haveing to copy-past a lot.
//  Note you MUST use N somewhere in this function lest the compiler optimize all N to 1 function
template<typename MANAGER, int N>
int callBackFunction(char* text) {
        typename MANAGER::functor_type* ftor = MANAGER::get(callBackFunction<MANAGER, N>);
        cout << "made it to the callBackFunction(" << N << ")" << endl;
        if (ftor!=NULL) {
            return (*ftor)(text);
        } 
        return 0;
        
}


//Use a typedef to name the particular manager we want
typedef CallBackManager<CallBackFn, FooMessageCall> FooMessageCallbackManager;

int main() {
    //Step #3: add some functions to the pool
    FooMessageCallbackManager::insert(callBackFunction<FooMessageCallbackManager, 0>);
    FooMessageCallbackManager::insert(callBackFunction<FooMessageCallbackManager, 1>);
    
    Foo foo1;
    Foo foo2;
    
    //Create some functors
    FooMessageCall fmc1(foo1);
    FooMessageCall fmc2(foo2);
    
    //allocate the functions
    CallBackFn fn1 = FooMessageCallbackManager::allocPointer(fmc1);
    CallBackFn fn2 = FooMessageCallbackManager::allocPointer(fmc2);
    
    // use them...
    IneedCallback(fn1);
    IneedCallback(fn2);
    
    //when we are done with one we want to release it
    FooMessageCallbackManager::freePointer(fn1);
    
    //So that we can use it again...
    Foo foo3(foo2);
    FooMessageCall fmc3(foo3);
    CallBackFn fn3 = FooMessageCallbackManager::allocPointer(fmc3);
    
    IneedCallback(fn3);
    
    return 0;
}



Its just a proof of concept so I have not really tested it but that should give you an idea of how this can be done.
Was This Post Helpful? 1
  • +
  • -

#11 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Re: Callback functions and classes

Posted 17 June 2011 - 01:48 AM

At the risk of reviving a dead topic, I've found an interesting workaround. What you can do is include static methods in your class that call the non-static member functions. You include a static pointer to your class within the class. You mark the static functions as the callback in another function (either the constructor or something else). An example of how I've implemented it:
class CGlutWrapper2D
{
	public:
		void begin (int& argc, char**& argv) const;

	private:
		static CGlutWrapper2D *_static_ptr;
		class callback;
		void construct () // Called within the class's multiple constructors
		{
			CGlutWrapper2D::_static_ptr = this;
		}
};
class CGlutWrapper2D::callback
{
	public:
		static inline void display () { _static_ptr->render(); }
		static inline void reshape (int w, int h)
		{ _static_ptr->reshape(w, h); }
		static inline void dkey (byte key, int x, int y)
		{ _static_ptr->key_down(key, x, y); }
		static inline void ukey (byte key, int x, int y)
		{ _static_ptr->key_up(key, x, y); }
		static inline void dkeyx (int key, int x, int y)
		{ _static_ptr->key_down_ex(key, x, y); }
		static inline void ukeyx (int key, int x, int y)
		{ _static_ptr->key_up_ex(key, x, y); }
};
void CGlutWrapper2D::begin (int& argc, char**& argv) const
{
	// Marking the callbacks
	glutDisplayFunc(CGlutWrapper2D::callback::display);
	glutIdleFunc(CGlutWrapper2D::callback::display);
	glutReshapeFunc(CGlutWrapper2D::callback::reshape);
	glutKeyboardFunc(CGlutWrapper2D::callback::dkey);
	glutKeyboardUpFunc(CGlutWrapper2D::callback::ukey);
	glutSpecialFunc(CGlutWrapper2D::callback::dkeyx);
	glutSpecialUpFunc(CGlutWrapper2D::callback::ukeyx);
}

Also I'd like to include a PM with Nick:

Quote

Quote

You replied helpfully to my topic on this subject earlier.
I wondered what would happen if I made my callback (which simply calls the global instance's function) inline. It seems to work -- have I commited any C++ crimes that will bite me later, has my compiler optimised out the inline (I read it's simply a suggestion to the compiler), or is it perfectly valid syntax?
Shame you didn't make this a topic in the forum since its kind of a neat answer.

It *seems* to work is the key here. Of course a callback function, or any function for which you use a function pointer, can not be inline! You need the address for where the function is stored and it must fallow a standard function protocol (so that the caller knows how to use it). So it just can't logically be done.

But the compiler allows it, and there are not errors when I run?

That is because "inline" is just a suggestion to the compiler. The compiler does not have to make the function actually inline. The compiler gets to choose, and if you use the function in some way that makes it impossible then of course it has no choice, it must make the function a normal callable function. By asking for the function's address you are assuring that the function will not be inline (at least the version used as a callback, there is no rule saying that a function must be one or the other, so the compiler can inline the code at other usage points).

So long story short -- if you really thought about it you would realize that the function could not have been inline.

This post has been edited by PlasticineGuy: 17 June 2011 - 01:50 AM

Was This Post Helpful? 0
  • +
  • -

#12 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2246
  • View blog
  • Posts: 9,236
  • Joined: 18-February 07

Re: Callback functions and classes

Posted 17 June 2011 - 09:36 AM

So I don't really get it... what would be the difference of just making all of the callback functions static?
Was This Post Helpful? 0
  • +
  • -

#13 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Re: Callback functions and classes

Posted 17 June 2011 - 06:16 PM

The rest of the class needs to be instantiated to be useful, and static functions can't access this without being passed as a direct parameter.
Was This Post Helpful? 0
  • +
  • -

#14 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2246
  • View blog
  • Posts: 9,236
  • Joined: 18-February 07

Re: Callback functions and classes

Posted 17 June 2011 - 10:52 PM

but if there is only 1 instance of the object then why not just use static methods -- heck why use a class at all?
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1