ticpete
ticpete

Reputation: 41

Passing void(*) in C++

I have a std::map which I'm trying to store void pointers for the values. The problem is, most of the pointer I'm trying to store are methods in a class and have different amount of params. I know for the params I can use a va list so thats not too much of a problem, the problem would be the actual pointer itself.

This is what I have:

class A 
{
public:
  A();
  void methodA(...);
};
class B
{
public:
  B();
  void methodB(...);
};
void method_no_class(...) { }

std::map<int, void(*)(...)> my_map;

my_map[0] = &method_no_class;
B* cb = new B();
my_map[1] = &cb->methodB; // will return error

Upvotes: 4

Views: 689

Answers (6)

Gunther Piez
Gunther Piez

Reputation: 30449

You should functionoids here. They can be used as a flexible and type safe replacement for function pointers with different signatures. A abstract base class is needed. It contains the actual function invocation with the common parameters, if there are any.

class Functioniod: public YourClass {
    virtual void execute(char d, common_parameters,...) = 0
}

For every function you want to use, you create a derived class. The constructor contains the function-specific parameters, and the execute() function the actual call. This execute function is later called instead of the function pointer. It needs to have the same signature in every functionoid. It could call something different in any other class too, of course.

class FuncA: public Functionoid {
    FuncA(int _a, float _b, string _c, function-specific-parameters...) {
        a = _a; b = _b; c = _c; 
    }
    void execute(char d, common-parameters,...) {
        call-to-member(d, a, b, c);
    }

    int a;
    float b;
    string c;
}

Now if you want to use this as a replacement for your member function pointer, you would do:

std::map<int, *Functionoid> my_map;

my_map[0] = new FuncA(someInt, someFloat, someString);
my_map[1] = new FuncB(some-other-parameters...);

and execute them with

my_map[0]->execute(common-parm);
my_map[1]->execute(common-parm);

Upvotes: 0

kenny
kenny

Reputation: 22404

You might want to look at method operaters ->, ::, and their friends. I'll try to find a better link but start here.

UPDATE: hopefully this is a better article for method pointers and operators.

Upvotes: 0

Tommy McGuire
Tommy McGuire

Reputation: 1253

Following up on Kamil Szot's answer, the C++ FAQ (and the book) is an excellent reference to the murky depths of C++ and object oriented programming in general. Section 33 addresses specifically the problem you are having:

In C++, member functions have an implicit parameter which points to the object (the this pointer inside the member function). Normal C functions can be thought of as having a different calling convention from member functions, so the types of their pointers (pointer-to-member-function vs. pointer-to-function) are different and incompatible.

Of course, the answer to your question is somewhat lacking in details.

Upvotes: 0

AareP
AareP

Reputation: 2387

Here's an example code to get you started. Haven't compiled it, so might require some tuning.

#define func(Instance,Method,Class) \
  (__int64(Instance)<<32 + __int64(&Class::Method))

#define invoke(Func,Method,Class) \
  invoke1(Func,(Class*)0)->*invoke2(Func,&Class::Method)

template<class Class>
Class* invoke1(__int64 Func,Class*)
{
  return (Class*)(int)(Func>>32);
}

template<class Method>
Method invoke2(__int64 Func,Method)
{
  return (Method)(int)Func;
}

------------ USAGE ------------
class B
{
  void methodB(int a,float b){}
};
std::map<int, __int64> my_map;

my_map[0] = func(cb,methodB,B);
invoke(my_map[0],methodB,B)(1,2.f);

Upvotes: -1

Kamil Szot
Kamil Szot

Reputation: 17817

Maybe this information my help you:

http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.1

Pointer to method is of different type than pointer to function. If you want to store them both in single collection you have to do manual casts.

Upvotes: 3

Aaron Digulla
Aaron Digulla

Reputation: 328780

The clean OO way would be to define a command interface. The interface would take an instance (of A or B) and all parameters. In the invoke() method, it would call the method of the instance.

You could then use a map of these command interfaces (just define a common subclass for them which defines the abstract invoke() method). The compiler would check all types and arguments for you, and you wouldn't have to use varargs.

Upvotes: 1

Related Questions