Any feedback on using C callbacks in openframeworks?

Hi,

I needed to use a C library in OF and digging through the code of the events implementation I came up with a wrapper to get pointers to member methods. As I’m not really 100% sure about this approach I was hoping any experienced dev could comment on my approach.

For example I have a method which defines a button with a callback:

button(ICON_COLOR, "Some text", callback);

To use this in OF the callback needs to be a static member:

button(ICON_COLOR, "Some text", ofApp::callback);

which is trouble as I don’t have access to ‘this’ anymore.

So I wrote a wrapper around it using a template:

template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
    template <typename... Args>
    static Ret callback(Args... args) { return func(args...); }
    static std::function<Ret(Params...)> func;
};

// Initialize the static member.
template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

template <class InstanceClass>
UIhandler ofxBluiCptr(InstanceClass  * instance, void (InstanceClass::*instanceMethod)(int, UIevent))
{
    Callback<void(int, UIevent)>::func = std::bind( instanceMethod, instance, std::placeholders::_1, std::placeholders::_2 );
    void (*c_func)(int, UIevent) = static_cast<decltype(c_func)>(Callback<void(int, UIevent)>::callback);
    return c_func;
}

So now I can do:

button(ICON_COLOR, "Some text", ofxBluiCptr(this, &ofApp::buttonPressed ))

As said I found the wrapper on SO and by digging in OF code. I hardly grasp what the wrapper is doing. Is this wrapper safe?

Thanks for any feedback.

Rg,

Arnaud

Unless I’m missing something, I dont’ think you can use your object the way you are showing here. The object is an automatic variable within the declaration of button(), meaning it will be disposed as soon as the line

button(ICON_COLOR, “Some text”, ofxBluiCptr(this, &ofApp::buttonPressed ))

has been executed.

In the end, you’d still need some kind of place to store this, e.g.,

ofxBluiCptr* myPtr = new ofxBluiCptr(this, &ofApp::buttonPressed );

but then you really haven’t gained anything since you still end up needing some kind of global or at least an instance variable.

Best Wishes,
Mark

Yeah, indeed I’m not gaining then. I just found out my wrapper doesn’t work also. It will work once. All registered callbacks will end up being the last registered callback.

Apparently there are some methods around it by using lambdas:

However that’s really nasty (c++11)code as well which I need to acquaint myself with first.

Apparently the usual approach is to have some userdata pointer added to the C library in which you can store a pointer to this. So I’m now looking into modifying the C library to accept an extra pointer to store the this context.

Unfortunately I don’t have time to flesh this out fully, but it would seem that you could make a CCallback mixin class and the make your own subclass of the OF button.

Below is a brief example of what I mean – typed straight into the browser and totally untested, but conceptually it should be sound.

This way the class is attached to button, gets automatically destroyed when the button gets destroyed etc.

class CCallback
{
	CCallback(
		Func	inCallback,
		void*	inUserData) :
			mCallback(inCallback),
			mUserData(inUserData)
	{
	}
	
	Func	mCallback;
	void*	mUserData;
	
	void	Perform()
			{
				(*mCallback)(mUserData);
			}

};

CMyButtonClass : public COFButton, public CCallback
{
	CMyButtonClass(
		int	    inButtonParam1,
		int     inButtonParam2,
		Func	inFunction,
		void*	inUserData) :
			COFButton(inButtonParam1, inButtonParam2),
			CCallback(inCallback, inUserData)
		{
		}
}