Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

demote boost::function to a plain function pointer

Tags:

c++

functor

boost

want to pass boost::bind to a method expecting a plain function pointer (same signature).

typedef void TriggerProc_type(Variable*,void*);
void InitVariable(TriggerProc_type *proc);
boost::function<void (Variable*, void*)> triggerProc ...
InitVariable(triggerProc);

error C2664: 'InitVariable' : cannot convert parameter 1 from 
'boost::function<Signature>' to 'void (__cdecl *)(type *,void *)'

I can avoid storing a boost::function and just pass the bound functor directly, but then I get similar error:

error C2664: 'blah(void (__cdecl *)(type *,void *))' : cannot convert parameter
1 from 'boost::_bi::bind_t<R,F,L>' to 'void (__cdecl *)(type *,void *)'
like image 357
Dustin Getz Avatar asked Nov 11 '08 22:11

Dustin Getz


4 Answers

Has anyone noticed that the accepted answer only works with trivial cases? The only way that function<>::target() will return an object that can be bound to a C callback, is if it was constructed with an object that can be bound to a C callback. If that's the case, then you could have bound it directly and skipped all of the function<> nonsense to begin with.

If you think about it, there isn't any magic solution to this. A C-style callback is stored as a single pointer which points to executable code. Any nontrivial boost::function<> is going to need at least two pointers: one to the executable code, the other to the data that's needed to set up the call (e.g. the 'this' pointer, in the case of a bound member function).

The right way to use boost::function and boost::bind with C callbacks is to create a shim function that satisfies the callback signature, figures out which function<> to call, and calls it. Usually C callbacks will have some kind of a void* for 'user data'; that's where you stash your function pointer:

typedef void (*CallbackType)(int x, void* user_data);
void RegisterCallback(CallbackType cb, void* user_data);

void MyCallback(int x, void* userData) {
  boost::function<void(int)> pfn = static_cast<boost::function<void(int)> >(userData);
  pfn(x);
}

boost::function<void(int)> fn = boost::bind(myFunction(5));
RegisterCallback(MyCallback, &fn);

Of course, if your callback signature doesn't include some kind of user data pointer, you're out of luck. But any callback that doesn't include a user data pointer is already unusable in most real-world scenarios, and needs to be rewritten.

like image 110
Ian Ni-Lewis Avatar answered Oct 22 '22 17:10

Ian Ni-Lewis


I think you want to use the target() member function of boost::function (isn't that a mouthful...)

#include <boost/function.hpp>
#include <iostream>

int f(int x)
{
  return x + x;
}

typedef int (*pointer_to_func)(int);

int
main()
{
  boost::function<int(int x)> g(f);

  if(*g.target<pointer_to_func>() == f) {
    std::cout << "g contains f" << std::endl;
  } else {
    std::cout << "g does not contain f" << std::endl;
  }

  return 0;
}
like image 38
coryan Avatar answered Oct 22 '22 18:10

coryan


can you get it working with bind?

cb_t cb = *g.target<cb_t>(); //target returns null

This is by design. Basically, since bind returns a completely different type, there's no way that this will work. Basically, a binder proxy object cannot be converted to a C function pointer (since it isn't one: it's a function object). The type returned by boost::bind is complicated. The current C++ standard allows no good way of doing what you want. C++0x will be fitted with a decltype expression which could be used here to achieve something like this:

typedef decltype(bind(f, 3)) bind_t;
bind_t target = *g.target<bind_t>();

Notice that this might or might not work. I have no way of testing it.

like image 4
Konrad Rudolph Avatar answered Oct 22 '22 19:10

Konrad Rudolph


see boost mailing page [boost] [Function] Problem using target() with boost::mem_fn and boost::bind

like image 2
Brent81 Avatar answered Oct 22 '22 18:10

Brent81