Jump to content
  • Advertisement
Sign in to follow this  
d000hg

passing class method pointers as function parameters

This topic is 4840 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

For a GUI system I'm making, I'd quite like to be able to register a method to respond to certain events. For instance a dialog gets notified when any of its controls get clicked, and then looks up to see if that event for that control has a handler defined. I define the child items of a dialog by either ID or the pointer to the item, so for example: I have a CustomDialog class derived from GUIDialog, which I have a method OnTestButtonClick(). I register a child button item with ID 123, and call RegisterEventHandler(123,LEFTBUTTON_CLICK,OnTestButtonClick). Are there any problems with passing pointers to member methods in this fashion? If I have a struct which maps events to handlers, like...
struct MessageHandlerLink
{
int itemID;
int EventType;
SOMETYPE functionpointer;
};
...then what type should SOMETYPE be? Thanks.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by d000hg
I have a CustomDialog class derived from GUIDialog, which I have a method OnTestButtonClick(). I register a child button item with ID 123, and call RegisterEventHandler(123,LEFTBUTTON_CLICK,OnTestButtonClick).

Are there any problems with passing pointers to member methods in this fashion?


Yes that is not the wright syntax for a pointer to member function, syntax would be &type::method_name. You should know that pointer to member functions != pointer to free functions and they are not interchangeable only the address of static member functions can be given to pointer to free-functions. Pointer to member functions can not be used on there own you need to use them with an instance of the type in question when invoked that is.

Quote:
Original post by d000hg
If I have a struct which maps events to handlers, like...
struct MessageHandlerLink
{
int itemID;
int EventType;
SOMETYPE functionpointer;
};
...then what type should SOMETYPE be?


for constant member functions:

return_type (type::*)(arg_type1, arg_type2.....) const;

for non constant member functions:

return_type (type::*)(arg_type1, arg_type2.....);

The problem is this is not going to work well unless you use templates but this isn't going to help much either and as i already mentioned you can't use them on there own you need an instance of the type in question. I suggest using boost::function or some form of generic signals & slots library like Boost.Signals

Share this post


Link to post
Share on other sites
Well, the first problem is that member functions have an implicit class pointer argument, so a member function with prototype void Class::function(); is actually effectively void Class::function(Class *);. So you can't call them without having an instance of the class. The second problem is a side effect of this, namely that a pointer-to-member-function must be declared to point to a member of a specific class and cannot then point to a member of another class, i.e.:
void (Class1::* function)(void) = &Class1::function; // OK
function = &Class2::function; // ERROR

The easiest solution to both these problems is to use boost::function and boost::bind. bind allows you to bind an argument to a function object, thus effectively transforming it into a function object which takes fewer parameters:
#include <boost/bind.hpp>
#include <boost/function.hpp>

class Class
{
public:
void memberFunction()
{
}
};

int main()
{
void (Class::* function)() = &Class::memberFunction;
Class instance;
// call function pointer on Class object instance
// equivalent to instance.memberFunction()
(instance.*function)();
boost::function1< void, Class * > boostFunction(&Class::memberFunction);
// call function pointer on Class object instance
// equivalent to instance.memberFunction()
boostFunction(&instance);
// bind instance to the first parameter of the function
boost::function0< void > boundFunction = boost::bind(&Class::memberFunction, instance);
// call function pointer using previously bound parameter
// equivalent to instance.memberFunction()
// (actually this is more correctly equivalent to Class(instance).memberFunction(), see bind docs for details).
boundFunction();
}

So make your SOMETYPE be the type of boost::function object which takes no parameters and returns whatever it is you want to return (I usually just use a void returning function), i.e. boost::function0< ReturnType > and pass your member functions as boost::bind(&Class::memberFunction, classInstance).

Enigma

Share this post


Link to post
Share on other sites
Mmmmm functors:-


class IFunctor
{
public:
virtual int Execute() = 0;
};

template <class T>
class MethodFunctor : public IFunctor
{
public:
typedef int( T::*MethodPtr )();
explicit MethodFunctor( T *a_inst, MethodPtr a_method ) : m_inst( a_inst ), m_method( a_method ) { }

int Execute() { return *m_inst.*m_method(); }
private:
T *m_inst;
MethodPtr m_method;
};


(Remember to change the sig of Execute / the MethodPtr typedef to suit your needs).

Alternatively, look into boost::function, boost::signals or sigslot.

Edit:- b10

Share this post


Link to post
Share on other sites
Thanks guys, particularly to Evolutional - I don't want to use an external library for this. Does it make any difference that all the pointers to class methods will belong to an instance of that class? ie MyDialog has pointers to MyDialog::OnButton1Clicked etc? Those functors would then not need a pointer to the object, or would they?

Are functors etc regarded as a dirty hack, or the standard/only/best way to implement the equivalent to MFC-style event-handling without using yucky macros?

Share this post


Link to post
Share on other sites
Quote:
Original post by d000hg
I don't want to use an external library for this.


Boost is no ordinary third-party library so you might want to reconsider that decision not just for boost::function but for other components aswell. Also boost::function is being reviewed (among other boost components too) to be part of the standard as we speak as std::tr1::function. The reason why we suggested it to you is boost::function is a generalized/generic function its not limited to member functions, any callable C++ entity can be used with it member functions, free functions, other functors so it gives you uniformity.

Quote:
Original post by d000hg
Does it make any difference that all the pointers to class methods will belong to an instance of that class? ie MyDialog has pointers to MyDialog::OnButton1Clicked etc?


Unless i miss understood you no.

Quote:
Original post by d000hg
Those functors would then not need a pointer to the object, or would they?


Yes it still needs to refer to an instance some where, you can't invoke a pointer to member function without an instance, in this case you could give it the this pointer

Quote:
Original post by d000hg
Are functors etc regarded as a dirty hack, or the standard/only/best way to implement the equivalent to MFC-style event-handling without using yucky macros?


Lets get one thing straight in evolutional's code & that article isn't the only form of functor and really evolutional's code isn't considered a functor in the context of C++ its more like an instance of the command design pattern. The concept of a functor is beyond language & language features, when somebody says "functor" in the context of C++ they generally refer to a user-defined type that overloads the function call operator and is therefore a callable entity it has nothing to do with things like inheritance or pointer to member functions but there is nothing that says you can't use them. std::tr1/boost::function is a generic function but as it overloads the function call operator it is also a functor aswell.

Therefore, to answer the question a functor is not a hack, it is not limited to a particular language it is a concept

[Edited by - snk_kid on August 18, 2005 7:53:25 AM]

Share this post


Link to post
Share on other sites
Just to clarify things a functor is a functional object, a function with state, function + object = functor. Its concept beyond any particular language.

If anybody is wondering whats the point of overloading the function call operator then the main point is static polymorphism in generic programming, in C++ consider this example:


template < typename UnaryFunction >
void foo(UnaryFunction f) {
f();
}

void foofunc() {}

struct foofunctor { void operator()() const {} };

int main() {

foo(foofunc);
foo(foofunctor());

}


This is static (compile-time) polymorphism.

std::tr1/boost::function kills two birds with one stone, it over comes the problem with subtle differences of C++ callable entities with uniformity and can be used in generic programming like the example above.

Share this post


Link to post
Share on other sites
Just to check that I really need such complicated stuff, I'll clarify what I want to do a bit more... below is a semi-pseudo example of what I have
class MyDialog : public GUIDialog
{
void OnButton1Click();
void OnButton2Click();
void OnScrollBarSelChange();

void OnChildEvent(DWORD ChildID,int Event)
{
if(ChildID == BUTTON1_ID && Event == LCLICK)
OnButton1Click();
else (ChildID == BUTTON2_ID && Event == LCLICK)
OnButton2Click();
else if(ChildID == SCROLL_LIST_ID && Event == SCROLL_SELCHANGE)
OnScrollBarSelChange();
}
}
OnChildEvent(...) is called by child items which are registered - the code for that isn't shown.
All I'm looking for is a way to automate that if... else if... logic, by having the ability to have
MyDialog::OnInitDialog()
{
RegisterEventHandler(BUTTON1_ID,LCLICK,OnButton1Click);
RegisterEventHandler(BUTTON2_ID,LCLICK,OnButton2Click);
RegisterEventHandler(SCROLL_LIST_ID,SCROLL_SELCHANGE,OnScrollBarSelChange);
}
Obviously RegisterEventHandler will do some fairly obvious stuff in theory... so must I use a delagate/functor design or is there some cunning way out? And is it possible to then derive MyNewDialog from MyDialog, with all the event handlers from MyDialog and some new ones?

Thanks for the help so far and any further thoughts/pseudo-code etc.

Share this post


Link to post
Share on other sites
Following on evolutional's ideas, you could attack this like a mini-state machine, and use funtionoids. Functionoids are like function pointers in C but are much more powerful.

Define the states, then prepare functionoid, then load an array of actions, and then just pass the signal. What you will find is that just about the only file you'll need to edit in future is the one that contains your state classes.

The beauty of this is that you can also pass ctor arguments as you set up your array, which can alter the behaviour of each action, then call the actions with your standard operator arguments.

For example:



// Superclass of functionoid.
class MyDialog
{
public:
virtual ~MyDialog() = 0;

// State definition.
enum state
{
state_1_,
state_2_,
state_3_,
.
.
}

// Class-based switch values.
static const int on_;
static const int off_;
static const int error_;

};

inline MyDialog::~MyDialog(){}

const int MyDialog::on_ = 1;
const int MyDialog::off_ = 0;
const int MyDialog::error_ = -1;

// Interface to state classes.
class MyDialogIface : public MyDialog
{
public:
virtual ~MyDialogIface() = 0;
virtual void action(int,state &) = 0;
};

inline MyDialogIface::~MyDialogIface(){}

// State classes...
class state_1 : public MyDialogIface
{
public:
virtual void action(int signal, state & current)
{
switch(signal)
{
case on_:
// insert some action code here.
current = state_2_; // now reset the state.
break;

case off_:
// insert some action code here.
current = state_1_;
break;

case error_:
// insert some action code here.
current = state_3_;
break;

default:
// do nothing.
}
}
};

class state_2 : public MyDialogIface
{
// As above.
.
.
};

class state_3 : public MyDialogIface
{
// As above.
.
.
};

// Action class, builds array of actions.
class MyDialogAction : public MyDialog
{
public:
MyDialogAction();
void operator()(int);
vector<MyDialogIface*>pVec_;
state object_state_;
};

MyDialogAction::MyDialogAction()
:pVec_(3),
object_state_(state_1_)
{
// This MUST be in the same order as the superclass 'enum state {}' members. for some
// really funky behaviour, you could add ctor arguments.
pVec_[0] = new state_1();
pVec_[1] = new state_2();
pVec_[2] = new state_3();
}

// The following operator automatically invokes action, and resets state. All
// you need to do is to pass the signal!
void MyDialogAction::operator()(int signal)
{
pVec_[object_state_]->action(signal,object_state_);
}

// Now when you want to use this at any time, just put something like this in your code:
.
.
.
MyDialogAction action;
while(signal)
{
action(signal);
}
.
.
.





Voila! When you want to build another, just think about the states, and build them. Everything else pretty much stays the same. You can even pass arguments to the constructors, to create special states.

A good source of information on functionoids can be found at http://www.parashift.com/c++-faq-lite/pointers-to-members.html.
Good Luck!

--random.

[Edited by - random_thinker on August 19, 2005 10:14:31 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!