简体   繁体   中英

lightweight boost::bind

I'm so sick of the pass-callback-data-as-void*-struct anti-pattern. Boost bind solves it nicely, but is an unacceptable dependency. What's a lightweight alternative? How would I write it myself as simply as possible?

First, I question your assertion that it's far too heavy for you to use.

Second, roll your own template, if you need to control the behavior.

Third, if you're afraid of rolling your own template, I question your ability to judge that boost::bind is too heavy for you to use.

Check out the fast delegate by Don Clugston. It's supposedly the fastest delegate you can find on most current platforms (compiles down to 2 assembly instructions.) Version 1.4+ gains some Boost.Bind compatibility.

I'm not familiar with boost:bind, but is it something like this?

#include <iostream>

void foo (int const& x) {
    std::cout << "x = " << x << std::endl;
}

void bar (std::string const& s) {
    std::cout << "s = " << s << std::endl;
}

template<class T>
void relay (void (*f)(T const&), T const& a) {
    f(a);
}

int main (int argc, char *argv[])
{
    std::string msg("Hello World!");
    relay (foo, 1138);
    relay (bar, msg);
}

Output --

x = 1138
s = Hello World!

A common C++ idiom is to use functors (ie objects that override operator()). The point is that you use a single object to encapsulate both the code to be called back, and the data on which that code will act. Whether the functor is hand-rolled, or generated using boost::bind and/or <functional>, probably doesn't make a whole lot of difference to runtime overhead.

So instead of:

typedef void (*cb)(void*);
void funcThatNeedsCallback(cb thecallback, void *thedata) {
    // blah blah
    thecallback(thedata);
}

do:

template<typename T>
void funcThatNeedsCallback(T &thefunctor) {
    // blah blah
    thefunctor();
}

Then the caller does:

struct MyFunctor {
    int mydata1;
    char *mydata2;
    void operator()(void) {
        // do something with mydata1 and mydata2
    }
};

MyFunctor mf = { value1, value2 };
funcThatNeedsCallback(mf);

Obviously if you prefer, you can make the members private and pass them in to a constructor rather than using the initializer list.

If you're worried about templates (for instance, if funcThatNeedsCallback is a lot of code which gets duplicated), then use an abstract class to define a virtual method which the parameter must have, and use that method as the callback:

class CallbackInterface {
    virtual void theCallback(void) = 0;
    virtual ~CallbackInterface() {} // just in case
};

void funcThatNeedsCallback(CallbackInterface &cb) {
    // blah blah
    cb.theCallback();
}

Boost.Function improved performance dramatically as of around 1.34 when used together with boost::bind. If you profiled with an old boost version, maybe do it again with a more recent one. boost::function got the ability to save small function objects in a small buffer allocated on the stack, instead of on the heap (using placement new).

See this mailing list message: http://lists.boost.org/Archives/boost/2006/01/98993.php .

People defending boost::binds speed have probably never written low latency trading systems or high speed graphics libraries.
Boost is a good general purpose library, not a speed optimised one. Some boost libraries (compared to tuned implementations) can be quite slow in comparison.

For functions/delegates, See http://www.codeproject.com/KB/cpp/fastdelegate2.aspx for a useful comparison.

Ciao.

There is libsigc++ . The license is LGPL, but the implementation is about what Boost.Signal does (I'm reading "too heavyweight" to mean "installing all of Boost is too heavyweight" not "Boost.Signal is too slow").

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM