In this problem, assume that we have handled all pointers in a nice, careful manner - to prevent question bloat I don't want to include my cleanup code here!
Let's say we have two classes, Foo
and Bar
with class definitions as follows:
class Foo
{
public:
Foo();
void fooFn();
};
class Bar
{
public:
Bar();
void barFn();
};
Assume that it is necessary that Foo
and Bar
have no inheritance relationship, but we need to call both fooFn
and barFn
in response to some stimulus. We can create a controller class with a container from which to call fooFn
and barFn
on specific instances of Foo
and Bar
- a static std::vector
for example - but we run into an issue: pointers to member functions of Foo
and Bar
instances are of different types.
By using a static vector< std::function<void()>* >
in the controller class, we can make a workaround. Foo
and Bar
instances can have a function which adds pointers to the vector through a lambda function which captures this
:
void Foo::registerFnPointer()
{
ControllerClass::function_vector.push_back( new [this](){ return this->fooFn(); } );
}
I have tested this method, and it appears to work without any problems. That said, I am concerned about the issues that could be caused by circumventing the type difference mentioned before... Am I worrying over nothing? Is there a better way to accomplish equivalent functionality?
The only problem I see has actually nothing to do with the functors but has to do with object lifetime. That is: I'm not sure how you ensure that you always de-register the functors registered with ControllerClass whenever an Foo or Bar instance gets destroyed.
You mention however that you do proper memory management.
In my opinion you do not need to store a pointer to function<void()>
, you can simply store function as value (that is have a vector<function<void()>>
).
Prior to C++11 and lambdas, to achieve the same effect you would have used a (boost) function also but you would would have used boost::bind with with the address of the fooFn and the first parameter bound to a pointer (or reference) to the Foo object instance.
This would have created an instance of the function that holds all of the information needed to call the fooFn method on the given object. You could then store the instance in a vector to call it at a later time (and had the same problem of making sure no boost::function
bound to a destroyed object remains registered)
Edit:
For the sake of completeness, the link to the Boost bind documentation specific for binding members: http://www.boost.org/doc/libs/1_56_0/libs/bind/bind.html#with_member_pointers
What you are doing is actually quite similar only that you are now using a lambda to capture the object pointer and to define the function to be called.
So I see no problem with what you are doing (other then the one I already mentioned).
You could use an adapter class. This might be overkill for what you're doing, but it may work.
The benefits of doing it this way are:
You don't have to change the original classes. Creating void Foo::registerFnPointer()
is ugly.
You don't have to use your static std::vector
.
You don't have to deal with function pointers.
So let's say you have two different classes like this:
struct Foo
{
void fooFn () {
std::cout << "Foo::fooFn ()" "\n" ;
}
};
struct Bar
{
void barFn () {
std::cout << "Bar::barFn ()" "\n" ;
}
};
The goal is to put them into a container and call their respective *Fn ()
member-functions.
An adapter would look something like this:
struct Adapter_Base
{
virtual ~Adapter_Base () {} ;
virtual void adapterFn () = 0 ;
};
template <typename T>
struct Adapter : Adapter_Base
{
T tVal ;
Adapter (const T &tVal) : tVal (tVal) {}
void adapterFn () ;
};
template <>
void Adapter <Foo>::adapterFn ()
{
tVal.fooFn () ;
}
template <>
void Adapter <Bar>::adapterFn ()
{
tVal.barFn () ;
}
And you could use it like this:
int main ()
{
std::vector <std::unique_ptr <Adapter_Base> > v1 ;
std::unique_ptr <Adapter_Base> u1 (new Adapter <Foo> (Foo ())) ;
std::unique_ptr <Adapter_Base> u2 (new Adapter <Bar> (Bar ())) ;
v1.push_back (std::move (u1)) ;
v1.push_back (std::move (u2)) ;
for (auto &adapter : v1) {
adapter->adapterFn () ;
}
return 0 ;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.