I’m aware of the tutorial at boost.org addressing this:
Boost.org Signals Tutorial, but the examples are not complete and somewhat over simplified. The examples there don’t show the include files and some sections of the code are a little vague.
Here is what I need:
ClassA raises multiple events/signals
ClassB subscribes to those events (Multiple classes may subscribe)
In my project I have a lower-level message handler class that raises events to a business class that does some processing of those messages and notifies the UI (wxFrames). I need to know how these all might get wired up (what order, who calls who, etc).
The code below is a minimal working example of what you requested. ClassA emits two signals; SigA sends (and accepts) no parameters, SigB sends an int. ClassB has two functions which will output to cout when each function is called. In the example there is one instance of ClassA (a) and two of ClassB (b and b2). main is used to connect and fire the signals. It's worth noting that ClassA and ClassB know nothing of each other (ie they're not compile-time bound).
#include <boost/signal.hpp>
#include <boost/bind.hpp>
#include <iostream>
using namespace boost;
using namespace std;
struct ClassA
{
signal<void ()> SigA;
signal<void (int)> SigB;
};
struct ClassB
{
void PrintFoo() { cout << "Foo" << endl; }
void PrintInt(int i) { cout << "Bar: " << i << endl; }
};
int main()
{
ClassA a;
ClassB b, b2;
a.SigA.connect(bind(&ClassB::PrintFoo, &b));
a.SigB.connect(bind(&ClassB::PrintInt, &b, _1));
a.SigB.connect(bind(&ClassB::PrintInt, &b2, _1));
a.SigA();
a.SigB(4);
}
The output:
Foo
Bar: 4
Bar: 4
For brevity I've taken some shortcuts that you wouldn't normally use in production code (in particular access control is lax and you'd normally 'hide' your signal registration behind a function like in KeithB's example).
It seems that most of the difficulty in boost::signal is in getting used to using boost::bind. It is a bit mind-bending at first! For a trickier example you could also use bind to hook up ClassA::SigA with ClassB::PrintInt even though SigA does not emit an int:
a.SigA.connect(bind(&ClassB::PrintInt, &b, 10));
Hope that helps!
Here is an example from our codebase. Its been simplified, so I don't guarentee that it will compile, but it should be close. Sublocation is your class A, and Slot1 is your class B. We have a number of slots like this, each one which subscribes to a different subset of signals. The advantages to using this scheme are that Sublocation doesn't know anything about any of the slots, and the slots don't need to be part of any inheritance hierarchy, and only need implement functionality for the slots that they care about. We use this to add custom functionality into our system with a very simple interface.
Sublocation.h
class Sublocation
{
public:
typedef boost::signal<void (Time, Time)> ContactSignal;
typedef boost::signal<void ()> EndOfSimSignal;
void endOfSim();
void addPerson(Time t, Interactor::Ptr i);
Connection addSignalContact(const ContactSignal::slot_type& slot) const;
Connection addSignalEndOfSim(const EndOfSimSignal::slot_type& slot) const;
private:
mutable ContactSignal fSigContact;
mutable EndOfSimSignal fSigEndOfSim;
};
Sublocation.C
void Sublocation::endOfSim()
{
fSigEndOfSim();
}
Sublocation::Connection Sublocation::addSignalContact(const ContactSignal::slot_type& slot) const
{
return fSigContact.connect(slot);
}
Sublocation::Connection Sublocation::addSignalEndOfSim(const EndOfSimSignal::slot_type& slot) const
{
return fSigEndOfSim.connect(slot);
}
Sublocation::Sublocation()
{
Slot1* slot1 = new Slot1(*this);
Slot2* slot2 = new Slot2(*this);
}
void Sublocation::addPerson(Time t, Interactor::Ptr i)
{
// compute t1
fSigOnContact(t, t1);
// ...
}
Slot1.h
class Slot1
{
public:
Slot1(const Sublocation& subloc);
void onContact(Time t1, Time t2);
void onEndOfSim();
private:
const Sublocation& fSubloc;
};
Slot1.C
Slot1::Slot1(const Sublocation& subloc)
: fSubloc(subloc)
{
subloc.addSignalContact(boost::bind(&Slot1::onContact, this, _1, _2));
subloc.addSignalEndSim(boost::bind(&Slot1::onEndSim, this));
}
void Slot1::onEndOfSim()
{
// ...
}
void Slot1::onContact(Time lastUpdate, Time t)
{
// ...
}
Did you look at boost/libs/signals/example ?
When compiling MattyT's example with newer boost (f.e. 1.61) then it gives a warning
error: #warning "Boost.Signals is no longer being maintained and is now deprecated. Please switch to Boost.Signals2. To disable this warning message, define BOOST_SIGNALS_NO_DEPRECATION_WARNING."
So either you define BOOST_SIGNALS_NO_DEPRECATION_WARNING to suppress the warning or you could easily switch to boost.signal2 by changing the example accordingly:
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
#include <iostream>
using namespace boost::signals2;
using namespace std;
Boost like QT provides its own implementation of signals and slots. Following are some example of its implementation.
Signal and Slot connection for namespace
Consider a namespace called GStreamer
namespace GStremer
{
void init()
{
....
}
}
Here is how to create and trigger the signal
#include<boost/signal.hpp>
...
boost::signal<void ()> sigInit;
sigInit.connect(GStreamer::init);
sigInit(); //trigger the signal
Signal and Slot connection for a Class
Consider a Class called GSTAdaptor with function called func1 and func2 with following signature
void GSTAdaptor::func1()
{
...
}
void GSTAdaptor::func2(int x)
{
...
}
Here is how to create and trigger the signal
#include<boost/signal.hpp>
#include<boost/bind.hpp>
...
GSTAdaptor g;
boost::signal<void ()> sigFunc1;
boost::signal<void (int)> sigFunc2;
sigFunc1.connect(boost::bind(&GSTAdaptor::func1, &g);
sigFunc2.connect(boost::bind(&GSTAdaptor::func2, &g, _1));
sigFunc1();//trigger the signal
sigFunc2(6);//trigger the signal
Above answer is great with signal2 same answer shoule be rewritten:
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
#include <iostream>
using namespace boost;
using namespace std;
struct ClassA
{
signals2::signal<void ()> SigA;
signals2::signal<void (int)> SigB;
};
struct ClassB
{
void PrintFoo() { cout << "Foo" << endl; }
void PrintInt(int i) { cout << "Bar: " << i << endl; }
};
int main()
{
ClassA a;
ClassB b, b2;
a.SigA.connect(bind(&ClassB::PrintFoo, &b));
a.SigB.connect(bind(&ClassB::PrintInt, &b, _1));
a.SigB.connect(bind(&ClassB::PrintInt, &b2, _1));
a.SigA();
a.SigB(4);
}
Related
I am trying to use the boost::signals2 functionalities in my program.
In my "Main Class" called "Solid" I have a member which I initialize inside the constructor body (so after the member initializer list) like this:
pointer_M = boost::make_shared<OtherClass>(/*parameters,...*/);
If the signal is triggered by some function I do not want to call a member of "OtherClass" (to which pointer_M points) but a member from "Solid", i.e. the class which initialized pointer_M just.
I tried the following:
boost::signals2::connection tria_signal = /*...*/.connect( boost::signals2::signal<void()>::slot_type(&Solid::call_this_function, pointer_M.get()).track(pointer_M) );
"call_this_function" is a member function of Solid. Unfortunately there are a bunch of error messages. "OtherClass" and "Solid" are not related by inheritance.
I would appreciated getting some advice how to fix my issue since I am very unexperienced with boost.
Best
But is that what I am trying to achieve possible at all?
The point is we can't tell without a clearer description. It sure sounds like a no-brainer: signals are specifically used to decouple callers and callees.
So let me just make up your "dummycode" for you. I'm going to sidestep the enormous type-overcomplication that you showed in that line:
boost::signals2::connection tria_signal =
/*...*/.connect(boost::signals2::signal<void()>::slot_type(
&Solid::call_this_function, pointer_M.get())
.track(pointer_M));
The whole idea of slots is that they generalize callables using type erasure. Just provide your callable in any compatible form and let the library deduce the arcane implementation types.
Live On Coliru
#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
struct OtherClass {
OtherClass(...) {}
boost::signals2::signal<void()> tria;
void test() {
if (!tria.empty())
tria();
}
};
struct Solid {
boost::shared_ptr<OtherClass> pointer_M;
Solid() {
pointer_M = boost::make_shared<OtherClass>(1,2,3);
auto tria_signal = pointer_M->tria.connect(
boost::bind(&Solid::call_this_function, this));
}
private:
void call_this_function() {
std::cout << "Solid was here" << std::endl;
};
};
int main() {
Solid s;
s.pointer_M->test();
}
Prints
Solid was here
Crystal Ball: Can We Guess The Problem?
Maybe we can guess the problem: it looked like you were putting effort into tracking the lifetime of the object pointed to by pointer_M. That's not useful, since that OtherClass owns the signal in the first place, meaning that all connections are disconnected anyways when the OtherClass disappears.
Correct Lifetime Management
What you likely want is for the connection to disconnect when the lifetime of the Solid object ends, not the OtherClass. In general, I'd suggest using scoped_connection here:
Live On Coliru
#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
struct OtherClass {
OtherClass(...) {}
boost::signals2::signal<void()> tria;
void test() {
if (!tria.empty())
tria();
}
};
struct Solid {
boost::shared_ptr<OtherClass> pointer_M;
Solid() {
pointer_M = boost::make_shared<OtherClass>(1,2,3);
tria_signal = pointer_M->tria.connect(
boost::bind(&Solid::call_this_function, this));
}
private:
boost::signals2::scoped_connection tria_signal;
void call_this_function() {
std::cout << "Solid was here" << std::endl;
};
};
int main() {
boost::shared_ptr<OtherClass> keep;
{
Solid s;
std::cout << "Testing once:" << std::endl;
s.pointer_M->test();
keep = s.pointer_M; // keep the OtherClass alive
} // destructs Solid s
std::cout << "Testing again:" << std::endl;
keep->test(); // no longer connected, due to scoped_connection
}
Prints
Testing once:
Solid was here
Testing again:
Simplify
In your case, the OtherClass is already owned by the Solid (at least it is created). It seems likely that having the shared-pointer is not necessary here at all:
Live On Coliru
#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
struct OtherClass {
OtherClass(...) {}
boost::signals2::signal<void()> tria;
void test() {
if (!tria.empty())
tria();
}
};
struct Solid {
Solid() : oc_M(1,2,3) {
tria_signal = oc_M.tria.connect(
boost::bind(&Solid::call_this_function, this));
}
void test() { oc_M.test(); }
private:
OtherClass oc_M;
boost::signals2::scoped_connection tria_signal;
void call_this_function() {
std::cout << "Solid was here" << std::endl;
};
};
int main() {
Solid s;
s.test();
}
Because the members are destructed in reverse order of declaration, this is completely safe.
Architecture Astronauting
If you know what you're doing, and the pointer_M actually needs to be shared, then likely you want to track that pointer. You should probably be considering making Solid also enable_shared_from_this. If you want to be really "Enterprise Grade Engineer™" about it, you could perhaps do something fancy with the aliasing constructor: What is shared_ptr's aliasing constructor for?
I have a bit of trouble understanding a std::bind call.
In the following example:
#include <functional>
#include <iostream>
#include <memory>
class Notifier
{
public:
Notifier(std::function<void(Notifier&)> on_notify)
:on_notify_(on_notify)
{ }
void notify()
{
if (on_notify_)
on_notify_(*this);
}
std::function<void(Notifier&)> on_notify_;
};
struct Manager
{
Manager()
{
n_ = std::make_unique<Notifier>(std::bind(&Manager::trigger, this));
}
void trigger()
{
std::cout << "notified" << std::endl;
}
std::unique_ptr<Notifier> n_;
};
int main()
{
Manager s;
s.n_->notify();
}
I don't understand how on_notify_(*this); calls back the functor with a Notifier& parameter, but the functor created by bind doesn't specify it.
The calls result correctly to the void notify() method, but I don't understand what exactly will be the functor created by bind to result in this.
If I were to write a lambda instead, I would need to specify the parameter, otherwise it would compile.
What kind of operation does bind here behind my back? :-)
std::bind basically ignores the invalid given argument according to this.
If some of the arguments that are supplied in the call to g() are not matched by any placeholders stored in g, the unused arguments are evaluated and discarded.
It might surprise you that when even more absurd arguments are provided, the binded functor can still successfully reach Manager::trigger() as follows:
#include <functional>
#include <iostream>
#include <memory>
// Some classes that have nothing to do with on_notify_
class AAA {};
class BBB {};
class Notifier
{
public:
Notifier(std::function<void(AAA&, BBB&)> on_notify)
:on_notify_(on_notify)
{ }
void notify()
{
if (on_notify_)
{
// Arguments not matching.
AAA a{};
BBB b{};
// Invoke with them.
on_notify_(a, b);
}
}
std::function<void(AAA&, BBB&)> on_notify_;
};
struct Manager
{
Manager()
{
n_ = std::make_unique<Notifier>(std::bind(&Manager::trigger, this));
}
void trigger()
{
std::cout << "it's also notified!" << std::endl;
}
std::unique_ptr<Notifier> n_;
};
int main()
{
Manager s;
s.n_->notify();
}
Live demo is here.
I want to have a class that can have a callback set to a pointer to member function. This means I need to store the address of the function, and the address of the object instance. The function should have the proper prototype and return value to what the callback expects.
I've played around with std::mem_fn and boost::bind (with the Boost Signals2 library), but it seems like I have to know the type of the class containing the callback function to store this information.
It seems like there should be a way to store a couple void* that would point to any object/function, but this obviously smells funny, loses type safety, etc.
Given a class SomeClass with a method some_method, I want to be able to do something like this:
SomeClass obj;
some_other_class.set_callback(&SomeClass::some_method, &obj);
Here is how I was able to accomplish this using Boost. Note that this uses Boost signals, and seems like overkill for a simple callback. Also, there is the issue of signals using "combiners" to determine the return value of the callback, since there are potentially multiple slots connected to a single signal. I only need support for a single callback. Also note that this is a complete compilable program:
#define _SCL_SECURE_NO_WARNINGS
#include <iostream>
#include <boost/bind.hpp>
#include <boost/signals2.hpp>
#include <string>
using namespace std;
struct MessageSource
{
boost::signals2::signal<void(const string &)> send_message;
typedef boost::signals2::signal<void(const string &)>::slot_type slot_type;
template<typename A, typename B>
boost::signals2::connection connect(A a, B b)
{
return send_message.connect(boost::bind(a, b, _1));
}
void send_msg(const string& msg)
{
send_message(msg);
}
};
struct Printer
{
void print(const string& msg) { std::cout << msg << std::endl; };
};
int main()
{
{
Printer p;
MessageSource s;
s.connect(&Printer::print, &p);
s.send_msg("test");
}
system("pause");
return 0;
}
I think the magic here is the fact that boost::bind() is able to handle a variety of types for its first argument. I just don't get how it can hold onto it in a private field of some sort without knowing the type...
Is this a case where a functor is really the right solution? It seems like member functions are so much more convenient to use...
Based on the comment by cdhowie above, I was able to come up with the following solution using std::function and std::bind:
#include <iostream>
#include <string>
#include <functional>
using namespace std;
struct MessageSource
{
function<void(const string& msg)> _callback;
template<typename A, typename B>
void connect(A func_ptr, B obj_ptr)
{
_callback = bind(func_ptr, obj_ptr, placeholders::_1);
}
void send_msg(const string& msg)
{
if (_callback)
_callback(msg);
}
void disconnect()
{
_callback = nullptr;
}
};
struct Printer
{
void print(const string& msg) { std::cout << msg << std::endl; };
};
int main()
{
{
Printer p;
MessageSource s;
s.connect(&Printer::print, &p);
s.send_msg("test");
s.disconnect();
s.send_msg("test again");
}
system("pause");
return 0;
}
How to specify lambda, std::bind result or any other std::function as argument for unix signal function?
I'm trying the following
std::function<void(int)> handler1 = std::bind(&cancellation_token::cancel, &c);
std::function<void(int)> handler2 = [&c](int) { c.cancel(); };
but it doesn't work, because both
handler1.target<void(int)>()
and
handler2.target<void(int)>()
return null
It works if I initialize handler with free function pointer
void foo(int) { ... }
std::function<void(int)> handler = foo;
but this is absolutely useless. I need to capture some local variables, so I need either bind or lambda.
Actually I understand why it doesn't work. Documentation says that target function returns a pointer to the stored function if target_type() == typeid(T), otherwise a null pointer. I don't understand how to make it work.
Any suggestions?
Since it's constructed by bind, or lambda with captured-data, you cannot convert it to free function, since target function works by typeid, std::function saves it in runtime, not for type T, with which function is templated. For std::bind it will be some library-type and for lambda it will be some unnamed type.
You can use a dispatcher-like approach associating signal numbers to std::functions through a map.
You just need a map to hold the std::functions accesible from a free function:
std::unordered_map<int, std::function<void(int)>> signalHandlers;
And a generic handler (free function) to map the signal number to the function:
void dispatcher(int signal) {
// this will call a previously saved function
signalHandlers.at(signal)(signal);
}
Implementation example
main.cpp
#include <iostream>
#include <thread>
#include <csignal>
#include "cppsignal.hpp"
int main() {
bool stop = false;
// set a handler as easy as this
CppSignal::setHandler(SIGINT, [&stop] (int) { stop = true; });
while (!stop) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Bye" << std::endl;
return 0;
}
cppsignal.cpp
#include <cstring> // strsignal
#include <csignal>
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <mutex>
#include "signal.hpp"
namespace CppSignal {
std::timed_mutex signalHandlersMutex;
std::unordered_map<int, std::function<void(int)>> signalHandlers;
// generic handler (free function) to set as a handler for any signal
void dispatcher(int signal) {
std::unique_lock<std::timed_mutex> lock(signalHandlersMutex, std::defer_lock);
if (!lock.try_lock_for(std::chrono::seconds(1))) {
// unable to get the lock. should be a strange case
return;
}
auto it = signalHandlers.find(signal);
if (it != signalHandlers.end()) {
it->second(signal);
}
}
void registerHandler(int signal, const std::function<void(int)>& handler) {
std::lock_guard<std::timed_mutex> lock(signalHandlersMutex);
signalHandlers.emplace(signal, handler);
}
// this is the only method you will use
void setHandler(int signal, const std::function<void(int)>& handler, int flags) {
// configure sigaction structure
struct sigaction action;
if (sigfillset(&action.sa_mask) == -1) {
throw std::runtime_error("sigfillset failed");
}
action.sa_flags = flags;
action.sa_handler = dispatcher;
// set handler for the signal
if (sigaction(signal, &action, nullptr) == -1 && signal < __SIGRTMIN) {
throw std::runtime_error("Fail at configuring handler for signal: " + std::string(strsignal(signal)));
}
registerHandler(signal, handler);
}
}
cppsignal.hpp
#ifndef __CPPSIGNAL_HPP
#define __CPPSIGNAL_HPP
#include <functional>
namespace CppSignal {
void setHandler(int signal, const std::function<void(int)>& handler, int flags=0);
}
#endif
sighandler_t is defined to be a pointer to a function with the following definition:
void func(int);
Since std::bind and lambdas return functors, it is not possible to directly use them as signal handler. As a workaround you can use your own wrapper functions like
class SignalHandlerBase
{
public:
virtual void operator(int) = 0;
};
template <class T>
class SignalHandler : public SignalHandlerBase
{
T t;
public:
SignalHandler(T _t) : t(_t) { }
void operator(int i)
{
t(i);
}
};
class SignalManager
{
int sig;
SignalHandlerBase *shb;
static void handlerFunction(int i)
{
shb(i);
}
public:
SignalManager(int signal) : sig(signal), shb(nullptr) { signal(signal, &handlerFunction); }
template <class T>
void installHandler(T t)
{
delete shb;
shb = new SignalHandler<T>(t);
}
};
Use global instances of SignalManager to manage individual signals
C++11 1.9 [intro.execution]/6:
When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which
are neither
of type volatile std::sig_atomic_t nor
lock-free atomic objects (29.4)
are unspecified during the execution of the signal handler, and the value of any
object not in either of these
two categories that is modified by the handler becomes undefined.
The only action you can realistically take portably in a signal handler is to change the value of a flag whose type is volatile std::sig_atomic_t or a lock-free std::atomic (Note that not all std::atomic objects are lock-free). Non-signal handling code can then poll that flag to respond to the occurrence of the signal.
N3787 has some interesting discussion about how to fix C++11 basically breaking signal handlers as a concept.
Suppose I have a function called subscribe() that takes a callback handler, which will be called when the event is triggered.
Now, I have another version, called subscribe2(). Everything is the same except that, when triggered, it needs to post it to an event queue. It is implemented using the original subscribe(), with a helper funciton called helper(). All it does is to bind the original handler and whatever additional arguments into a functor, and call postToEventQueue().
Now, I wonder if there's a way to eliminate the helper function, so that in subsribe2(), I can somehow package the postToTaskQueue() function and the original callback handler directly, and pass it to subscribe(). The reason is that I have a lot of different handler types, and it is tedious and tiring to introduce helper function all over the place. Afterall, boost::bind is supposed to return a new function given the original function, right? I am trying to generate the helper function directly with boost::bind.
One attempt is to say
subscribe(boost::bind(boost::bind(postToTaskQueue, boost::bind(_1, _2)), cb, _1));
in subscribe2(), but it doesn't work. Is it possible at all?
Please see detailed example code below. Thanks!
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <iostream>
typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;
void handler(int i){
std::cout << "i=" << i <<std::endl;
}
void subscribe(SomeCallback cb)
{
cb(100); //just invoke the callback for simplicity
}
void postToTaskQueue(Task t)
{
t(); // just invoke the task for simplicity
}
void helper(SomeCallback cb, int i)
{
Task t = boost::bind(cb, i);
postToTaskQueue(t);
}
void subscribe2(SomeCallback cb)
{
subscribe(boost::bind(helper, cb, _1));
// this does not work..
// subscribe(boost::bind(boost::bind(postToTaskQueue, boost::bind(_1, _2)), cb, _1));
}
int main()
{
subscribe(boost::bind(handler, _1));
subscribe2(boost::bind(handler, _1));
}
I have no answer. However, I've played with this for over an hour:
boost::bind
boost::apply<>
boost::protect
Maybe, just maybe, a more experienced boost developer could take it from here:
void subscribe2(SomeCallback cb)
{
using boost::bind;
using boost::protect;
using boost::apply;
bind(cb, 41)(); // OK of course
postToTaskQueue(bind(cb, 46)); // also fine
bind(postToTaskQueue, protect(bind(cb, 146)))(); // boost::protect to the rescue
postToTaskQueue(bind(apply<void>(), cb, 47));
bind(postToTaskQueue, protect(bind(apply<void>(), cb, 147)))();
The above prints
i=41
i=46
i=146
i=47
i=147
But, sadly, I can't seem to make this this thing parameterizing (as suggested should work in the documentation on composition using Nested Binds):
// but sadly, this appears to not work ...
auto hmm = bind(postToTaskQueue, bind(apply<void>(), cb, _1));
hmm(997); // FAIL
}
Here's a fully compiled demo showing the state of affairs: Live on Coliru
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <boost/bind/apply.hpp>
#include <iostream>
typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;
void handler(int i){
std::cout << "i=" << i <<std::endl;
}
void subscribe(SomeCallback cb)
{
cb(100); //just invoke the callback for simplicity
}
void postToTaskQueue(Task t)
{
t(); // just invoke the task for simplicity
}
void helper(SomeCallback cb, int i)
{
postToTaskQueue(boost::bind(cb, i));
}
void subscribe2(SomeCallback cb)
{
using boost::bind;
using boost::protect;
using boost::apply;
bind(cb, 41)(); // OK of course
postToTaskQueue(bind(cb, 46)); // also find
bind(postToTaskQueue, protect(bind(cb, 146)))(); // boost::protect to the rescue
postToTaskQueue(bind(apply<void>(), cb, 47));
bind(postToTaskQueue, protect(bind(apply<void>(), cb, 147)))();
// but sadly, this appears to not work ...
auto hmm = bind(postToTaskQueue, bind(apply<void>(), cb, _1));
//hmm(997); // FAIL
}
int main()
{
subscribe (boost::bind(handler, _1));
subscribe2(boost::bind(handler, _1));
}
You are binding a function (helper) that itself does a bind. That mean you are (indirectly) binding bind itself. This is the key insight. The solution is to write a little bind function object wrapper that can itself be bound. Here's what my solution looks like:
#include <utility>
#include <iostream>
#include <boost/function.hpp>
#include <boost/phoenix/bind.hpp>
#include <boost/phoenix/core/argument.hpp>
using boost::phoenix::placeholders::_1;
typedef boost::function<void(int)> SomeCallback;
typedef boost::function<void()> Task;
struct bind_t
{
template<typename Sig>
struct result;
template<typename This, typename ...A>
struct result<This(A...)>
{
typedef decltype(boost::phoenix::bind(std::declval<A>()...)) type;
};
template<typename ...A>
auto operator()(A &&...a) const -> decltype(boost::phoenix::bind(std::forward<A>(a)...))
{
return boost::phoenix::bind(std::forward<A>(a)...);
}
};
bind_t const bind = {};
void handler(int i)
{
std::cout << "i=" << i <<std::endl;
}
void subscribe(SomeCallback cb)
{
cb(100); //just invoke the callback for simplicity
}
void postToTaskQueue(Task t)
{
t(); // just invoke the task for simplicity
}
void subscribe2(SomeCallback cb)
{
subscribe(bind(postToTaskQueue, bind(bind, cb, _1)));
}
int main()
{
subscribe(::bind(handler, _1));
subscribe2(::bind(handler, _1));
}
I switched to Phoenix's bind because it lets you bind polymorphic function objects (which bind above is).
This solution requires decltype. It also uses variadics, but that can be faked with overloads up to N arguments. Rvalue refs are also a convenience that can be done without with a little more work.