1

DISCLAIMER I'm not allowed to use BOOST or any other library, only standard.

In my class Foo I've a template function foo, which takes 2 parameters: a pointer-to-object and a pointer-to-this-object-member-function. The foo function works with pointer of any class, as you can see. I do NOT know, what class will be passed to it. This function creates an instance of a template structure.

template <typename TypeName>
struct Bar
{
    void(TypeName::*action)();
    TypeName* objPtr;
};



template <typename TypeName> void Foo::foo ( void(TypeName::*action)() , TypeName* objPtr )
{
    Bar <TypeName> bar;
    bar.action = action;
    bar.objPtr = objPtr;
};

My question is: how do I store objects of Bar, created in foo, so I can iterate through them later and call the pointer-to-an-object-member-function like this:

(BarStorage[i].objPtr->*BarStorage[i].action)();
Rodrigo Guedes
  • 1,169
  • 2
  • 13
  • 27
Kolyunya
  • 5,973
  • 7
  • 46
  • 81

1 Answers1

5

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.

Fiktik
  • 1,941
  • 16
  • 25
  • man, this is an **AWESOME** idea. Gonna try this out! Thanks much. – Kolyunya Aug 28 '12 at 11:36
  • @Fiktik, may I ask you some questions concerning your code, please? My compiler g++ doesn't know this syntax with `override` key-word. Is it a new standard or what? I just type `virtual void invoke() { (objPtr->*action)(); }`. Second: why do you use `unique_ptr`, not just `vector myBars;`. Third, what is `auto`? Again some new feature in new standard? I just type `Bar* bar = new Bar ( );` – Kolyunya Aug 28 '12 at 13:51
  • And the main question: why `myBars[x]->invoke();` calls overridden function and not the base function? `myBars` stores pointers to `BarBase`s, so why not the base function is called? – Kolyunya Aug 28 '12 at 13:54
  • @Kolyunya I've added the explanation to the answer. – Fiktik Aug 28 '12 at 15:04
  • @Fiktik, thanks for help and maybe you will find a minute to look at the continuation of the problem here? http://stackoverflow.com/questions/12178280/c-operating-with-template-objects – Kolyunya Aug 29 '12 at 13:03