Use type erasure:
class BarBase {
virtual void invoke() = 0;
};
template<typename TypeName>
class Bar final : public BarBase {
void(TypeName::*action)();
TypeName* objPtr;
virtual void invoke() override { (objPtr->*action)(); }
};
class Foo {
std::vector<std::unique_ptr<BarBase>> myBars;
/* ... */
};
template <typename TypeName>
void Foo::foo ( void(TypeName::*action)() , TypeName* objPtr)
{
auto bar = new Bar<TypeName>();
bar->action = action;
bar->objPtr = objPtr;
myBars.emplace_back(bar);
};
then simply call myBars[x]->invoke();
To answer questions in comments.
What's up with override
? Explicitly say that you override virtual function from a base class. This is not really necessary here, but is considered good practice. For more details see Wikipedia article.
Its a new feature from C++11, some compilers supported this feature in one way or another for a long time, but its been standardized only now. GCC should support this feature from version 4.7 - you have to use command line parameter -std=c++0x
.
What's up with unique_ptr
? It makes life easier. When you allocate new Bar<XXX>()
, you must at some point say delete
to free that memory. If you put the pointer in a resource managing object like unique_ptr
, you no longer need to worry about deleting it. With vector<BarBase*>
you would have to declare a destructor in Foo
that does something like:
for each element in myBars
delete element
If you use vector<unique_ptr<BarBase>>
, you don't need to worry about deleting anything. When vector
is destroyed at end of Foo
's life, it will destroy its elements - and unique_ptr
deletes the memory it contains in its own destructor.
This is also C++11 feature - addition to standard library. You don't have to use it (but then make sure to delete everything at the end).
What's up with auto
? Instead of repeating the same type twice (Bar<Type> * bar = new Bar<Type>();
), just use it once and let the compiler deduce the correct type of the variable based on the type of the initializer. It behaves exactly the same, its just less typing and looks better :)
Also C++11 feature, supported in GCC since v4.4.
Why myBars[x]->invoke()
does the right thing? Because invoke
is declared virtual in BarBase
. This means the method executed is chosen based on the dynamic type (the real type during execution) of myBars[x]
, not static type. For in-depth explanation, see Wiki. There is a minor run-time overhead with this mechanism, but it can't be helped when dealing with dynamic types.