Sign in to follow this  
Mantear

Function pointers

Recommended Posts

Greetings, I'm throwing together a simple GUI for my program, and one of the things I'm trying to do is link items between the GUI and the rest of my program. Check boxes are linked to bools, enums are linked to radio buttons, etc. However, I'm trying to link a button to a function. What do I need to grab the function pointer and assign it to a member variable, so when the button is pressed I can call the function pointed to by the member variable? Thanks.

Share this post


Link to post
Share on other sites
Depends on whether your pointer points to a free function or to a non-static member function.

And no, you can't have both. That's why there are things like boost::function.

Share this post


Link to post
Share on other sites
Hmm, not sure what you mean. The function I'm trying to point to is a member function of the class that the function pointer is also in.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
if you have a function declared: int doSomething(double x, double y)
the applicable data type you could assign this to would look like:

int (*fn)(double,double)

thus if I am interpreting your question correctly, in your button class you might have a mutator function looking something like

public:
void setAction(void (*newFunc)(void)) { actionFunc = newFunc; }
private:
void (*actionFunc)(void);

should you desire to associate arbitrary void functions with the pressing of the button. As far as more general function pointers, as in no specific predefined parameter types, I have never seen them, and logically it doesn't make sense that they would exist for this type of application (otherwise how would you handle passing parameters...)

Share this post


Link to post
Share on other sites
I'm getting this error:
cannot convert from 'void (__thiscall MyClass::*)(void)' to 'void (__cdecl *)(void)

Here's my setup:
// MyClass.h
typedef void (*FunctionPtr) (void);
class MyClass {
public:
void MyFunction(void);
private:
FunctionPtr MyFunctionPtr;
};

// MyClass.cpp
MyClass::MyClass(void)
{
MyFunctionPtr = MyFunction;
}

void MyClass::MyFunction(void)
{
// do stuff
};

I've tried using '&', MyFunctionPtr = &MyFunction, but the compiler does like that. Putting MyClass:: in front of MyFunction on the assignment doesn't seem to do anything, either. What do I need to do differently?

Share this post


Link to post
Share on other sites
The short answer is
typedef void (*FunctionPtr) (void);
should be
typedef void (MyClass::*FunctionPtr) (void);


For a longer answer, see my reply to this question.

Share this post


Link to post
Share on other sites
EDIT: beaten

If you're looking for a quick-and-dirty solution, you could define a subclass ICallback in your button class. This subclass would contain a pure virtual OnClick(CButton &) function. Your button would then have an ICallback * member, which would be initialized to a class deriving from CButton::ICallback, which would in turn implement the OnClick() function. It's fairly simple compared to designing your own functor library, but the whole architecture is fairly ugly IMO.

Example:

class CButton
{
public:
class ICallback
{
public:
virtual void OnClick(CButton &) = 0;
};

void Click()
{
if (mCallback)
mCallback->OnClick(*this);
}

ICallback * mCallback;
};

class CMyApp : public CButton::ICallback
{
public:
void Init()
{
mButton.mCallback = this;
}

void Test()
{
mButton.Click();
}

void OnClick(CButton & button)
{
printf("Clicked!\n");
}

private:
CButton mButton;
};




This is my own (far-from-perfect, but meets my needs) "functor" library:

template<typename TReturn, typename TArgs>
class CFunctor;

template<typename TReturn, typename TArgs>
class IFunction;

template<typename TReturn, typename TArgs>
class CGlobalFunction;

template<class TParent, typename TReturn, typename TArgs>
class CMemberFunction;

template<typename TReturn, typename TArgs>
class IFunction
{
public:
virtual void Delete() = 0;
virtual TReturn Call(TArgs args) = 0;
};

template<typename TReturn, typename TArgs>
class CGlobalFunction : public IFunction<TReturn,TArgs>
{
public:
void Delete() { delete this; }

typedef TReturn (* FuncSig)(TArgs);

CGlobalFunction(FuncSig func)
{
mFunction = func;
}

~CGlobalFunction()
{
}

TReturn Call(TArgs args)
{
return mFunction(args);
}

private:
FuncSig mFunction;
};

template<class TParent, typename TReturn, typename TArgs>
class CMemberFunction : public IFunction<TReturn,TArgs>
{
public:
void Delete() { delete this; }

typedef TReturn (TParent::* FuncSig)(TArgs);

CMemberFunction(TParent * parent, FuncSig func)
{
mParent = parent;
mFunction = func;
}

~CMemberFunction()
{
}

TReturn Call(TArgs args)
{
return (mParent->*mFunction)(args);
}

private:
TParent * mParent;
FuncSig mFunction;
};

template<typename TReturn, typename TArgs>
class CFunctor
{
public:
CFunctor()
{
mFunction = null;
}

~CFunctor()
{
Unbind();
}

void Unbind()
{
if (mFunction)
{
mFunction->Delete();
mFunction = null;
}
}

void Bind(TReturn (* func)(TArgs))
{
Unbind();
if (func)
mFunction = new CGlobalFunction<TReturn,TArgs>(func);
}

template<class TParent>
void Bind(TParent * parent, TReturn (TParent::* func)(TArgs))
{
Unbind();
if (parent && func)
mFunction = new CMemberFunction<TParent,TReturn,TArgs>(parent,func);
}

operator bool()
{
return (mFunction != null);
}

TReturn operator()(TArgs args)
{
return mFunction->Call(args);
}

private:
IFunction<TReturn,TArgs> * mFunction;
};

// Specializations

template<typename TReturn>
class IFunction<TReturn,void>
{
public:
virtual void Delete() = 0;
virtual TReturn Call() = 0;
};

template<typename TReturn>
class CGlobalFunction<TReturn,void> : public IFunction<TReturn,void>
{
public:
void Delete() { delete this; }

typedef TReturn (* FuncSig)();

CGlobalFunction(FuncSig func)
{
mFunction = func;
}

~CGlobalFunction()
{
}

TReturn Call()
{
return mFunction();
}

private:
FuncSig mFunction;
};

template<class TParent, typename TReturn>
class CMemberFunction<TParent,TReturn,void> : public IFunction<TReturn,void>
{
public:
void Delete() { delete this; }

typedef TReturn (TParent::* FuncSig)();

CMemberFunction(TParent * parent, FuncSig func)
{
mParent = parent;
mFunction = func;
}

~CMemberFunction()
{
}

TReturn Call()
{
return (mParent->*mFunction)();
}

private:
TParent * mParent;
FuncSig mFunction;
};

template<typename TReturn>
class CFunctor<TReturn,void>
{
public:
CFunctor()
{
mFunction = null;
}

~CFunctor()
{
Detach();
}

void Unbind()
{
if (mFunction)
{
mFunction->Delete();
mFunction = null;
}
}

void Bind(TReturn (* func)())
{
Unbind();
if (func)
mFunction = new CGlobalFunction<TReturn,void>(func);
}

template<class TParent>
void Bind(TParent * parent, TReturn (TParent::* func)())
{
Unbind();
if (parent && func)
mFunction = new CMemberFunction<TParent,TReturn,void>(parent,func);
}

operator bool()
{
return (mFunction != null);
}

TReturn operator()()
{
return mFunction->Call();
}

private:
IFunction<TReturn,void> * mFunction;
};




Example:

class CButton
{
public:
void Click()
{
if (OnClick)
OnClick(*this);
}

CFunctor<void,CButton &> OnClick;
};

class CMyApp
{
public:
void Init()
{
mButton.OnClick.Bind(this,MyClickHandler);
}

void Test()
{
mButton.Click();
}

void MyClickHandler(CButton & button)
{
printf("Clicked!\n");
}

private:
CButton mButton;
};

Share this post


Link to post
Share on other sites
Hmm, ok, I appear to be able to pass around the function pointer now, but how do I call it? If I just state the variable that the function pointer is assigned to, it skips over it. If I put '()' after it, I get the compile error, 'term does not evaluate to a function'.

Also, what if I try to pass the function pointer between different classes? Is that even possible?

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
Depends on whether your pointer points to a free function or to a non-static member function.

And no, you can't have both.


Okay, while technically true, you actually can have both! You can have amazing flexibility (far beyond member function pointers, which are throttled by inheritance), departing slightly from the standard... Here is an excellent article with the gritty details.


Share this post


Link to post
Share on other sites
Quote:
Original post by ajas95
Okay, while technically true, you actually can have both! You can have amazing flexibility (far beyond member function pointers, which are throttled by inheritance), departing slightly from the standard... Here is an excellent article with the gritty details.


That is just a poor man's generic signals & slots or std::tr1/boost::function which has already been mentioned.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this