4

I have a caller function like this:

template<typename T, void (T::*method)()>
void CallMethod(T *object){
    (object->*method)(args);
}

and while this works perfectly:

void (*function)(A *) = &CallMethod<A, &A::method>;

this code does not compile with error on the second line:

void (A::*method)() = &A::method;
void (*function)(A *) = &CallMethod<A, method>;

Is there any way to fix it? I need the CallMethod template to take a constant pointer to a method that is stored in a variable.

Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96
  • 2
    Is that the actual code that doesn't compile? What is "method" here? you are assigning a member function called method to a variable also called method plus the member function variable is declared wrong.. – CashCow Dec 08 '10 at 13:32
  • 2
    why don't you use std::tr1::function/boost::function with bind for this? – DaVinci Dec 08 '10 at 13:35
  • 'method' is just some method of A. It is not an actual code, I tried to simplify it as much as possible. – Juraj Blaho Dec 08 '10 at 13:47
  • 2
    boost:function/bind won't work if what he actually wants is a function that takes an A* parameter. I think he is trying to dynamically create one. – CashCow Dec 08 '10 at 14:02
  • @CashCow: you can `bind` a member function to an object, get the function pointer from the `function` returned, and work with that... – rubenvb Dec 08 '10 at 14:31
  • @rubenvb he has a C interface that wants a regular function, not a member function. You can use a boost::function still but need an adapter like the one in my answer to get it to work in a C interface. – CashCow Dec 08 '10 at 14:46
  • @Cashcow: see the first link in my updated answer to see a method of getting a function pointer from a `function` object. – rubenvb Dec 08 '10 at 14:47

5 Answers5

3

All template parameters must be known at compile time. So if method is really a variable and not a particular function, there is no way to do what you want.

Instead, you may be able to make a template struct which has a pointer to member function as a member and implements an operator() which acts similarly to CallMethod<A, method>.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • That is not an option. I need a function pointer as a result, because I am passing objects to plain C. – Juraj Blaho Dec 08 '10 at 13:46
  • In that case, pass something with enough information as the object pointer. Using an `A*` pointer as the only function argument is not enough to figure out what method to call. See @CashCow's answer. – aschepler Dec 08 '10 at 14:23
2

At this point I have ascertained that you have an API that takes a function and a pointer and you need to supply it such. I assume that you must always supply it an A* pointer?

If it is a very generic callback but must be a function (can't be a boost::function) and must be a pointer (possibly void*) then you need a function a bit like this:

struct GenericHolder
{
   boost::function0<void> func;
};

void GenericCallback( void * p )
{
   GenericHolder * holder = static_cast< GenericHolder * >(p);
   holder->func();
   delete holder;
}

As in this case I am calling delete at call time, so I have assume we call new at invoke time, i.e. when you build up your pointer on which your call is made. Of course it might not be that your pointer you pass is deleted on first call, so manage the lifetime appropriately.

If you are in control of the "other side" then don't do this by design but let that side hold the boost::function and just call it.

Memory management can still be an issue you need to take care of. For example, when you call boost::bind it wraps things in a struct behind the scenes for you. If these are pointers you allocated with new you need to delete them at sometime. If they are references they must still be valid at the point of call. pointers to local variables can also be a problem. shared_ptrs are ideal, of course. And the bug tracking to the boost::bind error is very very hard to find if you use that concept a lot.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • Thanks, this would work. It is not exactly what I wanted. I wanted to generate functions (method wrappers) at compile time. Your method does the work at runtime which I consider not so elegant, but it would still work. – Juraj Blaho Dec 09 '10 at 07:37
1

It looks like you're trying to implement an interface via templates. It might be possible, but wouldn't it be nicer to have a base class with a virtual function, and use a base class pointer to access the virtual function via a simple function call?

Header:

class AbstractBase
{
public:
    virtual void func() = 0;
}
class SubClass : public AbstractBase
{
public:
    void func();
}

Source file:

void SubClass::func()
{
    std::cout << "Virtual function called.\n";
}

Example usage:

int main()
{
    AbstractBase* base;
    base = new SubClass();
    base->func(); // virtual function call through base pointer will use SubClass's implementation
    return 0;
}

You can store a vector of pointers (or smart pointers if you use boost or C++0x) which you can loop through and use this to do all kinds of SubClass dependent stuff.

Alternatively, use boost::function or std::function (C++0x) which is an object containing the member function in question and pass that as a template parameter, using the object instance as a first argument. Which comes down to a workaround of the above.

UPDATE: Seeing that you need a plain C function pointer, there are some tricks that might imapct runtime performance in c++0x or boost: bind, function::target and so forth...

You will need this to get a function pointer out of a std/boost::function, and this to bind the first argument to an object. bind acts at runtime, so maybe a template would be better in this case...

Community
  • 1
  • 1
rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • I need a plain function pointer instead of a method call, so I could pass it to C. – Juraj Blaho Dec 08 '10 at 14:00
  • `function::target` will not work here. [This answer][1] to your linked question is essentially the same as @CashCow's to this one. [1] http://stackoverflow.com/questions/282372/demote-boostfunction-to-a-plain-function-pointer/3453616#3453616 – aschepler Dec 08 '10 at 16:27
0

What if 2 methods have the exact same signature ?

The template can not distinguish on it (it selects on type, not value) and thus the same generated template function will call the first method or the second, would you bet your hand on it ?

You can use some tricks to make a different type at compile time, using the __LINE__ macro, but it involves mixing macro with current code which will be hard to follow. This code:

template<typename T, void (T::*method)(), int>
void CallMethod(T *) { ... }
#define MakeCallMethod(X, Y) &CallMethod<X, X::&Y, __LINE__>

// This will work (it does not without the __LINE__ trick, ptr1 == ptr2 in that case)
typedef void (*ptrFunc)(A *);
ptrFunc ptr1 = MakeCallMethod(A, method);
ptrFunc ptr2 = MakeCallMethod(A, otherMethodWithSameSignature);

Assert(ptr1 != ptr2);

However you'll not be able to save the pointer to a method in a variable and make a "auto wrapper" from it. It's now runtime, you need a runtime solution for this.

xryl669
  • 3,376
  • 24
  • 47
0

First, please create typedefs http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.5

Second,

void (*function)(A *) = &CallMethod<A, method>;

In this case, method is a variable and variables can not be template arguments. I haven't tested it but perhaps you could try...

void (*function)(A *) = &CallMethod<A, void (A::*method)()>;
Andrew White
  • 52,720
  • 19
  • 113
  • 137
  • 1
    You can't create templated typedefs, though. – aschepler Dec 08 '10 at 13:39
  • No but he could do typedef void (A::*AMethod)(); and then we could refer to that type as just AMethod – Andrew White Dec 08 '10 at 13:43
  • Templates can be instantiated by constant variables. The question is how this could be achieved for method pointers. – Juraj Blaho Dec 08 '10 at 13:44
  • First, you didn't define a constant variable. Second, does my second line not work? – Andrew White Dec 08 '10 at 13:47
  • @Andrew I've no idea what your proposed line is supposed to even mean. Best remove it again, it makes no sense at all. Your answer was fine until the "I haven't tested ..." part, as far as I'm concerned. It then turned into a [guess answer](http://meta.stackexchange.com/questions/64308/try-xyz-answers). – Johannes Schaub - litb Dec 08 '10 at 13:57
  • The second line is not working. So how could I make a const pointer to member function? 'void (A::* const method)()' does not help. – Juraj Blaho Dec 08 '10 at 13:58
  • @Johannes Schaub: I was simply trying to replace *method* with a type. Ironically, your link for a guess answer has -8 votes – Andrew White Dec 08 '10 at 14:07