Storing polymorphic function pointers

Started by
2 comments, last by rpiller 10 years, 4 months ago

Hi,

In my game engine, I have an object hierarchy consisting of instances of classes that inherit my base class STObject. STObject can handle registration of object parameters (to make my game object parameters editable in my game editor, and for automatic serialization).

In STObject, I have a method called


registerParameter(STParameterPointer parameter, const std::string& name, const std::string& description)

that lets STObject subclasses register their parameters. STParameterPointer is in fact a boost::variant, that can store pointers to all types that I use in my STObjects. Now, instead of registering a variable directly, I'd like to store a pointer to get/set functions. For example, instead of doing something like


registerParameter(&position, "name", "description")

I want to be able to do


registerParameter(&MySTObjectSubclass::pos(), &MySTObjectSubclass::setPos, "name", "description");

I'm using C++11 and I've had a look at std::function. However, a function pointer stored in that way does not work polymorphically, which means that I can't just store a new type "STObject::setIntFunction" : I would have to create one type in my variant for each STObject subclass that wants to register a pair of getter/setters.

I'd appreciate any help on how to achieve what I want to do. If my question is unclear, please let me know and I'll explain further. Thanks in advance!

Advertisement

So I have event code that I use all the time. It basically allows you to link class member functions, which basically seems like what you are doing. I posted the file below that I use all the time. It has different versions based on parameter lists but in this case I think all you care about is the return type? These all have void return types, but you should be able to modify one of these to have a return template type and possible use that?

The normal usage is:


Event1<int> OnClick;
 
// ClickFunction would be defined to have 1 parameter of type int
OnClick.Bind(objPtr, &MyObject::ClickFunction);
 
// calls all member functions that were bound to this event
OnClick.Raise(5);

So if your register takes an Event object pointer and you create a new event ctor that allows you to define the object ptr and object function it could look like:

// if called from within the subclass

registerParameter(new Event(this, &MySTObjectSubClass::SetPos), "name", "description");

In your base class you have a list of Event types to store these all. You can then cycle through them and call Raise() on them all. Now, in this state it means the return type of all of these functions would have to be the same because you'd have to define this in your list. list<Event<int>*>. To solve this problem you might be able to use a property class that I sometimes use. This isn't 100% complete but it shows that you can store variables of different type into 1 container. You could probably store different Event<int> Event<float> etc objects in this property type container and loop over them all and call them? You might have to template you registerParameter function so that when we call it we can tell it what the return type is like: registerParameter<int>(new Event(this, ..)). This part is a little fuzzy. You'd have to play with it.

I know this is complicated and might not work, but trying to give you ideas and possibilities. I don't have the time right now to play with these 2 ideas to see if it'll work for your specific need but it seems like it might.


#pragma once
#include <list>
 
using namespace std;
 
 
class TFunctor0
{
public:
virtual void Call()=0;
};
 
template <class TClass>
class TSpecificFunctor0 : public TFunctor0
{
private:
void (TClass::*fpt)();
TClass* pt2Object;         
public:
TSpecificFunctor0(TClass* _pt2Object, void(TClass::*_fpt)())
{ pt2Object = _pt2Object;  fpt=_fpt; }
 
virtual void Call()
{ (*pt2Object.*fpt)(); }
};
 
class Event0
{
public:
list<TFunctor0*>  mCaller;
 
template<class Target>
void Bind(Target* t, void (Target::*fnPtr)())
{ mCaller.push_back(new TSpecificFunctor0<Target>(t,fnPtr)); }
 
void Clear()
{ mCaller.clear(); }
       
void Raise()
{
list<TFunctor0*>::reverse_iterator iter;
 
for (iter = mCaller.rbegin(); iter!= mCaller.rend(); iter++)
{
(*iter)->Call();
}
} 
};
 
//===============================================================================
 
template<class P>
class TFunctor1
{
public:
virtual void Call(P var1)=0;
};
 
template <class TClass, class param1>
class TSpecificFunctor1 : public TFunctor1<param1>
{
private:
void (TClass::*fpt)(param1);
TClass* pt2Object;         
public:
TSpecificFunctor1(TClass* _pt2Object, void(TClass::*_fpt)(param1))
{ pt2Object = _pt2Object;  fpt=_fpt; }
 
virtual void Call(param1 var1)
{ (*pt2Object.*fpt)(var1); }
};
 
template<class T1>
class Event1
{
public:
list<TFunctor1<T1>* >  mCaller;
 
template<class Target>
void Bind(Target* t, void (Target::*fnPtr)(T1))
{ mCaller.push_back(new TSpecificFunctor1<Target, T1>(t,fnPtr)); }
       
void Raise(T1 V1)
{
list<TFunctor1<T1>*>::reverse_iterator iter;
 
 
for (iter = mCaller.rbegin(); iter!= mCaller.rend(); iter++)
{
(*iter)->Call(V1);
}
}
 
void Clear()
{
mCaller.clear();
}
};
 
//===============================================================================
 
template<class P, class Q>
class TFunctor2
{
public:
virtual void Call(P var1, Q var2)=0;
};
 
template <class TClass, class param1, class param2>
class TSpecificFunctor2 : public TFunctor2<param1, param2>
{
private:
void (TClass::*fpt)(param1, param2);
TClass* pt2Object;         
public:
TSpecificFunctor2(TClass* _pt2Object, void(TClass::*_fpt)(param1, param2))
{ pt2Object = _pt2Object;  fpt=_fpt; }
 
virtual void Call(param1 var1, param2 var2)
{ (*pt2Object.*fpt)(var1, var2); }
};
 
template<class T1, class T2>
class Event2
{
public:
list<TFunctor2<T1, T2>* >  mCaller;
 
template<class Target>
Event2(Target* t, void (Target::*fnPtr)(T1, T2))
{ mCaller.push_back(new TSpecificFunctor2<Target, T1, T2>(t,fnPtr)); }
 
Event2(){}
 
template<class Target>
void Bind(Target* t, void (Target::*fnPtr)(T1, T2))
{ mCaller.push_back(new TSpecificFunctor2<Target, T1, T2>(t,fnPtr)); }
       
void Raise(T1 V1, T2 V2)
{
list<TFunctor2<T1, T2>*>::reverse_iterator iter;
 
 
for (iter = mCaller.rbegin(); iter!= mCaller.rend(); iter++)
{
(*iter)->Call(V1, V2);
}
} 
 
void Clear()
{
mCaller.clear();
}
};
 
 
//===============================================================================
 
template<class P, class Q, class Y>
class TFunctor3
{
public:
virtual void Call(P var1, Q var2, Y var3)=0;
};
 
template <class TClass, class param1, class param2, class param3>
class TSpecificFunctor3 : public TFunctor3<param1, param2, param3>
{
private:
void (TClass::*fpt)(param1, param2, param3);
TClass* pt2Object;         
public:
TSpecificFunctor3(TClass* _pt2Object, void(TClass::*_fpt)(param1, param2, param3))
{ pt2Object = _pt2Object;  fpt=_fpt; }
 
virtual void Call(param1 var1, param2 var2, param3 var3)
{ (*pt2Object.*fpt)(var1, var2, var3); }
};
 
template<class T1, class T2, class T3>
class Event3
{
public:
list<TFunctor3<T1, T2, T3>* >  mCaller;
 
template<class Target>
void Bind(Target* t, void (Target::*fnPtr)(T1, T2, T3))
{ mCaller.push_back(new TSpecificFunctor3<Target, T1, T2, T3>(t,fnPtr)); }
       
void Raise(T1 V1, T2 V2, T3 V3)
{
list<TFunctor3<T1, T2, T3>*>::reverse_iterator iter;
 
 
for (iter = mCaller.rbegin(); iter!= mCaller.rend(); iter++)
{
(*iter)->Call(V1, V2, V3);
}
}
 
void Clear()
{
mCaller.clear();
}
};
 


#include <string>
#include <map>

using namespace std;

class Prop
{
public:
virtual ~Prop()
{
}
};

template<typename T>
class Property : public Prop
{
private:
T data;
public:
virtual ~Property()
{
}

Property(T d)
{
data = d;
}

T GetValue()
{
return data;
}
};

class Properties
{
private:
map<string, Prop*> props;
public:
~Properties()
{
map<string, Prop*>::iterator iter;

for(iter = props.begin(); iter != props.end(); ++iter)
delete (*iter).second;

props.clear();
}

template<typename T>
void Add(string name, T data)
{
props[name] = new Property<T>(data);
}

template<typename T>
T Get(string name)
{
Property<T>* p = (Property<T>*)props[name];

return p->GetValue();
}
};

int main()
{
Properties p;

p.Add<int>("age", 10);
int age = p.Get<int>("age");

return 0;
}

Thanks for pointing out that what I'm trying to do, passing parameters to methods in my object hierarchy, is precisely what messaging is. So I'll start by implementing events and message passing in my game engine; when that is in place, setting parameters will come naturally.

I will look into different messaging solutions before deciding on an approach, which means I'll also try to read and understand your solution. I may get back to you with questions, if that's OK. ;)

Yeah for sure. I'm not saying mine is the best way, but it's just a way that I like to handle messages/events :) I figured this might at least help get the ideas flowing.

This topic is closed to new replies.

Advertisement