Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast to it boost::bind(&myClass::fun, this, _1, _2, _3) to typedef void (*fun)(arg1, arg2, arg3)?

In lib Bullet there is defined a type:

typedef void (*btNearCallback)(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo);

in there docs there is presented a sample of usage (page 23):

void MyNearCallback(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo) {
        // Do your collision logic here
        // Only dispatch the Bullet collision information if you want the physics to continue
        dispatcher.defaultNearCallback(collisionPair, dispatcher, dispatchInfo);
}

I copied this sample code into my class defention so my class got this function and I shall be capable to do such casts like:

    dispatcher->setNearCallback(boost::bind(&BulletAPIWrapper::MyNearCallback, this, _1, _2, _3));

instead of C like dispatcher->setNearCallback(MyNearCallback); from Bullet tutorial.

Yet my VS2010 sp1 gives me an error:

Error   44  error C2664: 'btCollisionDispatcher::setNearCallback' : cannot convert parameter 1 from 'boost::_bi::bind_t<R,F,L>' to 'btNearCallback'

So I wonder how to cast boost::bind to such typedef?

Is it possible having having static class function (or at least global function like):

void MyNearCallback(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo, BulletAPI* api) {
}

call dispatcher->setNearCallback( boost::bind(MyNearCallback, _1, _2, _3, this));

because it results in nearly same error for me:

Error   44  error C2664: 'btCollisionDispatcher::setNearCallback' : cannot convert parameter 1 from 'boost::_bi::bind_t<R,F,L>' to 'btNearCallback'

I also tried as described here:

template<unsigned ID,typename Functor>
boost::optional<Functor> &get_local()
{
    static boost::optional<Functor> local;
    return local;
}

template<unsigned ID,typename Functor>
typename Functor::result_type wrapper(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo)
{
    return get_local<ID,Functor>().get()(collisionPair, dispatcher, dispatchInfo);
}

template<typename ReturnType>
struct Func
{
    typedef ReturnType (*type)(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo);
};

template<unsigned ID,typename Functor>
typename Func<typename Functor::result_type>::type get_wrapper(Functor f)
{
    (get_local<ID,Functor>()) = f;
    return wrapper<ID,Functor>;
}

struct NearCallbackWrapper
{
    class BulletAPI;
    void MyNearCallback(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo) {
        std::cout << "called" << std::endl;
    }
};

//....
dispatcher->setNearCallback( get_wrapper<0>(  boost::bind(&NearCallbackWrapper::MyNearCallback,this) ) );

yet I got such error out from it:

error C2664: 'btCollisionDispatcher::setNearCallback' : cannot convert parameter 1 from 'void (__cdecl *)(btBroadphasePair &,btCollisionDispatcher &,const btDispatcherInfo &)' to 'btNearCallback'
like image 225
DuckQueen Avatar asked Feb 10 '13 23:02

DuckQueen


1 Answers

Why do you think you "should be able to cast like.."? The setNearCallback certainly expects a normal function pointer to be passed, while BIND produces something completely different..

The fact that Bind produces a "callable thing" that does not "require 'this' pointer" does NOT mean that it has produced a plain function!

To properly handle bound member functions you still need space of at least two pointers, while a normal function pointer is ONE pointer. All sane *) APIs that allow you to register callbacks also allow you to pass some 'userdata' along with the callback - in such cases you can use that to create small wrapper that will redirect the call to your bound member function. This has been discussed in many places already.. please see for example: https://stackoverflow.com/a/3453616/717732

If you cannot pass ANY additional data along with the callback, I mean, if the callback registration allows you to provide only the callback pointer, then it's almost dead end. You cannot escape that unless you do some more-or-less ugly or risky workarounds with ie. global static data or dynamic code generation..

*) this is purely personal point of view. By 'sane' I mean 'object-friendly'. Low-level APIs very often are not strictly meant to be, but rather they try to be as resource-conserving as possible - and therefore it forces you to do the dirty work yourself, because they wanted to really save those 4/8 bytes. Sometimes, sometimes, this actually has a huge impact - they may pass the 4/8b callback easier, it easy copiable as it fits into a single register (while pointer+userdata would take 2+ registers), operations on it are "more atomic" etc. However, most often, this is done to painfully highlight that there is only ONE callback possible to be registered. In such cases, it actually makes you very little difference, whether it will be bound member function of some object, or just a global static function: all in all, there can be only one, so eh, whatever, just make it work. If this is the case, then just use global static variable for the object pointer and small wrapper. Well, except for the aesthetics..:

static MyObject* target;
void wrapper(..params..)
{
    target->method_to_be_called(..params..);
}

// then, register it:

target = &object_to_be_called;  // \ there can be only one object
setCallback(&wrapper);          // / and only one callback registered
like image 174
quetzalcoatl Avatar answered Nov 15 '22 17:11

quetzalcoatl