code
code

Reputation: 5642

Mimicking Special JavaScript Callback in C++

I am having trouble particular with this type of callback implementation in C++. In the below code, I would like to store a callback into "OnRenderingComplete". However, I'm having trouble remembering the "ad" variable and "callback" parameters of "adOnRenderingComplete". How is this implemented in C++?

var adOnRenderingComplete = function(ad, callback) {

};

var setAdPlaybackCallbacks = function(ad, callback) {

    OnRenderingComplete = function() { adOnRenderingComplete.call(this, ad, callback); };
};

In JavaScript, it seems that this is possible since there can be an embedded function storing the parameters of "ad" and "callback"... but I am not sure how this would be done in C++. I' would ultimately like to call OnRenderingComplete(), with it "remembering" the parameters "ad" and "callback".

I am having a hard time. I can't seem to figure this out. The only thing close that I can think of is to define a class within the function and pass the function of that class to OnRenderingComplete. But even so, the below is wrong and does not compile.

void testCall(void (*callback)())
{
    callback();
}

void test1()
{
    class blah
    {
    public:
        int a;
        int b;
        void c()
        {
            cout << "a * b = " << a*b << endl;
        };
    };

    blah ex;
    ex.a = 5;
    ex.b = 3;

    void(*OnRenderingComplete)() = ex.c;
    testCall(OnRenderingComplete);    // I would like to have this print the value of a*b
}

Essentially, I am attempting to capture "ex.c" inside the callback variable "OnRenderingComplete". But, at the same time, I'd like to capture the value of ex.a and ex.b so that ex.c can be called while "remembering" the data for "a" and "b". After making the call "testCall(OnRenderingComplete)", I would like the function at the top for testCall() to be able to print the value of a*b.

Upvotes: 0

Views: 66

Answers (1)

Synxis
Synxis

Reputation: 9388

Several solutions (click on the titles for a live example):

C++11:

#include <functional>

struct MyClass
{
    typedef std::function<void (int, bool)> CallbackType;
    typedef std::function<void (int, float, bool)> AnotherCallbackType;
    CallbackType callback;

    void setCallback(AnotherCallbackType c, float param)
    {
        callback = [&](int a, bool b) { c(a, param, b); };
        // Calling the callback:
        callback(42, false);
        // With lambdas, it is almost like in Javascript,
        // you can embed all parameters you want. ex:
        std::function<void()> func = [&](){ c(2, param, true); };
        func(); // will call c(2, param, true);
    }
};

C++03, with Boost:

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

struct MyClass
{
    typedef boost::function<void (int, bool)> CallbackType;
    typedef boost::function<void (int, float, bool)> AnotherCallbackType;
    CallbackType callback;

    void setCallback(AnotherCallbackType c, float param)
    {
        callback = boost::bind(c, _1, param, _2);
        // Calling the callback:
        callback(42, false);
        // This will call: c(42, param, false)
        // The 'param' float parameter has been retained.
        // It is possible to retain all parameters when binding, ex:
        boost::function<void()> func = boost::bind(c, 2, param, true);
        func(); // will call c(2, param, true);
    }
};

C++03, without Boost:

struct MyClass
{
    typedef void (MyClass::* CallbackType)(int, float);
    CallbackType callback;

    void onRenderCallback(int param1, float param2) {}
    void setCallback(CallbackType c)
    {
        callback = c;
        // Calling the callback:
        (this->*callback)(42, 3.14f);
        // Note that it is possible to embed the parameters
        // but that will need to create a class, basically it means you will reimplement Boost.Bind...
    }
};

Now, back to your code:

void(*OnRenderingComplete)() = ex.c;

For pointer to function, I seriously advise you to use typedefs:

typedef void (*OnRenderingCompleteType)();
OnRenderingCompleteType callback = ...;

Then, ex.c has the following type: void (blah::*)(). Note the blah::*, it is because this is a pointer-to-member-function, not a pointer-to-function (member functions are special functions). This is the reason why the assignment does not compile.

Upvotes: 2

Related Questions