views:

241

answers:

5

Hi!

Before I was trying to map my classes and namespaces, by using static calls I succeded and now I need to map the functions of my classes because they will be used dinamically.

Firstly I was thinking to hardcode in the constructor so I can assign a std:map with the string of the name of function pointing to the function itself.

for example:

class A{
  int B(){
      return 1;
  }
};

int main(){
  A *a = new A();
  vector<string, int (*)()> vec;

  vector["A.B"] = a.B;
}

by that I have mapped the function B on A class, I know that I only mapped the function the instance and thats B is not static to be globally mapped.
but thats what I need, at somepoint someone will give me a string and I must call the right function of an instance of a class.

My question is if I only can do that by hardcoding at the constructor, since this is a instance scope we are talking or if there is somehow a way to do this in the declaration of the function, like here for namespaces and classes: http://stackoverflow.com/questions/1691473/c-c-somehow-register-my-classes-in-a-list

Thanks,
Joe

A: 

just found(using google) a topic to the same question I had with an answer.

http://stackoverflow.com/questions/405432/what-is-the-simplest-way-to-create-and-call-dynamically-a-class-method-in-c

I didn't try it yet but makes sense, I will ask again later if it doesn't work

ty!

Joe

Jonathan
A: 

I must call the right function of an instance of a class.

You need to call a specific method on an existing instance, or you need to create an instance of the appropriate type and call the method?

If it's the former, then you need a std::map or similar that lets you look up instances from their names.

If it's the latter, that's basically what serialization frameworks need to do in order to create the correct type of object when de-serializing, the object that knows how to read the next bit of data. You might take a look at how the Boost serialization library handles it:

boost.org/doc/libs/1_40_0/libs/serialization/doc/serialization.html

Tim Sylvester
+1  A: 

If I understand you correctly, you're going to have a class like:

struct Foo
{
   int bar();
};

And the user will input a string like "Foo::bar", and from that string you need to call the member function Foo::bar?

If so, it's rather awkward to code a flexible solution in C++, due to the static type system. You can use an std::map where the key is a string, and the value is a member function pointer, (or std::mem_fun_t object), but this will only work on a single class, and only on member functions with the same signature.

You could do something like:

#include <iostream>
#include <map>
#include <functional>

struct Foo
{
 int bar() { std::cout << "Called Foo::bar!" << std::endl; }
};

int main()
{
 std::map<std::string, std::mem_fun_t<int, Foo> > m;
 m.insert(std::make_pair("Foo::bar", std::mem_fun(&Foo::bar)));

 Foo f;
 std::map<std::string, std::mem_fun_t<int, Foo> >::iterator it = m.find("Foo::bar");
 std::mem_fun_t<int, Foo> mf = it->second;
 mf(&f); // calls Foo::bar
}
Charles Salvia
+3  A: 

If I understand you correctly, you want your map to store a pointer that can be used to call a member function on an instance, the value being chosen from the map at run time. I'm going to assume that this is the right thing to do, and that there isn't a simpler way to solve the same problem. Quite often when you end up in strange C++ backwaters it's a sign that you need to look again at the problem you think you have, and see whether this is the only way to solve it.

The problem with using an ordinary function pointer is that a non-static member function is not an ordinary function. Suppose you could point to a member function with an ordinary function pointer, what would happen when you dereferenced that pointer and called the function? The member function needs an object to operate on, and the syntax doesn't provide a way to pass this object in.

You need a pointer to member, which is a slightly obscure feature with relatively tricky syntax. While an ordinary pointer abstracts an object, a pointer to member abstracts a member on a class; the pointer specifies which class member should be called, but not which object to obtain the member from (that will be specified when the pointer is used). We can use it something like this:

class B;

class A
{
    B some_function()
    { /* ... */ }
};


B (A::* myval)() = A::some_function;

Here myval is a variable that indicates one of the members of class A, in this case the member some_function (though it could point to any other member of A of the same type). We can pass myval round wherever we want (e.g. storing it in an STL container, as in your example) and then when we want to call the function, we specify the instance it should be called on in order to locate the function:

A some_a;

B newly_created_b = (some_a.*myval)();

This works for a particular case, but it won't solve your general issue, because member pointers contain the class they refer to as part of the definition. That is, the following two variables are of entirely different types:

B (Foo::* first_variable)() = Foo::some_function;
B (Bar::* second_variable)() = Bar::some_function;

Even though both functions can produce a B when called without arguments, the two values operate on different classes and therefore you can't assign a value of one type to a variable of the other type. This of course rules out storing these different types in a single STL container.

If you're committed to storing these in a container, you'll have to go with a functor-based solution like Charles Salvia proposes.

Tim
Not that I'm recommending this of course but ... well ... you *could* store both types in a collection if you were willing to do some unsafe casting. Or, even more dangerously, start playing with 'offsetof'.
Eric
A: 

Are you doing this in some kind of tight loop where you need the efficiency of a good map? If so, then member function pointers (as you linked to above) is a good way to go. (At least it is after you work around the problem @Tim mentioned of keeping member function pointers to different types in the same collection ... let the language abuse begin!)

On the other hand, if this is in code that's user-driven, it might be more legible to just be totally uncool and write:

if( funcName=="A.b" )
{
    A a;
    a.b();
} else
// etc etc etc

For the higher-performace case, you can supplement the same approach with a parse step and some integer constants (or an enum) and use a switch. Depending on your compiler, you might actually end up with better performance than using member function pointers in a map:

switch( parse(funcName) )
{
    case A_b:
    {
       A a;
       a.b();
    }
    break;
}

(Of course this breaks down if you want to populate your list of possibilities from different places ... for example if each class is going to register itself during startup. But if you have that kind of object infrastructure then you should be using interfaces instead of pointers in the first place!)

Eric
This approach is error prone and doesn't scale - and you can't really do it all via interfaces if you need to e.g. expose functionality to some scripting language.
Georg Fritzsche
@gf: Every solution to the problem (as asked) is error prone and scales badly. At least this approach is type-safe and uses simple syntax.
Eric
@Eric: Sorry if that sounded too harsh, but there are better solutions then explicit case-switching that are also type-safe. With explicit switch you do, imo, repitive and redundant work (where you might make errors) when a central one-line registration in combination with a standard dispatching/invoke function would suffice :)
Georg Fritzsche
But really, Eric's solution is the simplest one that works if the OP wants to invoke member functions from different classes, or with different function signatures. Otherwise, the OP will need some pretty elaborate infrastructure to set this up.
Charles Salvia
Charles, the OP talks about the functions of his classes - which sounds like he does want to do this for quite some or all. Of course, if its only about a few functions in a few classes Erics solution is the simplest.
Georg Fritzsche
@gf: No worries at all. Case-switching certainly does break down in lots of scenarios -- distributed registration like I had mentioned, and varying signatures like you did. I just like to see the simplest possible answer get a place at the table!
Eric