In the software development, the pattern when one object has send a message to another object, where the object that has to be called is given to the first class somehow (usually in constructor), is quite common. In C#, the delegates serve exactly this purpose, but in C++ it is a different story.
The most common way of implementing the class-to-class callback in C++ is to define an interface, which the class that receives the event must implement:
class IInterface
{
public:
virtual void CallbackMethod() = 0;
}
class MyClass:
public IInterface
{
public:
virtual void CallbackMethod()
{
// do something useful
}
class Publisher
{
private:
IInterface* m_pCallee;
public:
Publisher(IInterface* pCallee):
m_pCallee(pCallee)
{
}
private:
void SomeMethod()
{
m_pCallee->CallbackMethod();
}
}
}
It isn’t exactly rocket science, but this method has some downsides, and some of them are quite nasty and not obvious at all. Those are:
- You may need to define a new interface every time you need a callback function with new signature. Could partly be solved by making the interface class template
- Each class that interested in callbacks needs to implement corresponding interface(s)
- In some situations you may end up with a single object that can receive callbacks from many different publishers.
The last one could make you wish you never become a software programmer. The architecture, which seemed so beautiful just moments before, starts to fall into pieces once you find yourself in situation when you realize that your class-subscriber needs to receive same callbacks from different sources and act differently depending on who the caller is.
Let me explain it on the example. Imagine you have a class that implements that callback interface. You also have a lot of providers that take that interface to inform the subscribers, for example, about quote changes. But in your class, you need to track changes on, say, 10 quotes, and depending on which of those 10 changes, take different actions. So, what ’s your options?
Of course, you could just add an additional parameter to callback method that specifies the sender, but it is not pretty at all, as the subscriber class now faces a new challenge of keeping track of all publishers it is subscribed too. Not mentioning the mess in the callback method needed to get all stuff to work!
Ideally, you want each of those providers to call different methods of your subscriber class, but in a general case it would mean 10 different interfaces and changes in all publishers, which may not be possible and I don’t even want to talk about it. The other option would be to write the proxy, which implements the basic callback interface, subscribes to the only provider and then re-routes the call to certain method on your class.
Thankfully, there is the far better option. boost::bind and boost::function are here to help.
class Publisher{
public:
typedef boost::function<void (int)> typeCallbackType;
void Subscribe(typeCallbackType cb)
{
m_cb = cb;
}
private:
typeCallbackType m_cb;
void SomeDataProcessingMethod()
{
// ....
m_cb(currentPrice);
}
}
And here is a good thing: your subscriber class does not have to implement any custom interface anymore:
class MySubscriber
{
public:
void CalledWhenPriceChanges(int newPrice)
{
// make profit here
}
}
to establish a subscription, simply use boost::bind:
Publisher* pPublisher = GetPublisherFor("someTicker");
MySubscriber Subscriber;
pPublisher->Subscribe(boost::bind(&MySubscriber::CalledWhenPriceChanges, &Subscriber, _1));
That’s all, folks!
Now, once the pPublisher has a new data, a CalledWhenPriceChanged() method of Subscriber instance of a class MySubscriber will be called. It is easy to see how additional publisher could be added if you want to monitor two tickers.
It would not only help solving the problem of multiple publishers-one subscriber. This approach will also do a great job saving you time from not having to define numbers of callback interfaces for different types of data that needs to be passed from publisher to subscriber.
You also might think about situations where one object has to call different callbacks depending on what exactly it is doing, which leads as to events/signals kind of framework, like Boost::signals (it is using boost::function as well
)
But I would prefer some special alias or overloaded “Subscribe” method over using “bind”. Code has to be self explanatory, which means it has to show what it is doing, not how it is doing that. “bind” however just show us the internals and take the attention off the important bit, registering the method as callback. Hiding “bind” away will make code cleaner. It doesn’t mean you should use it under the cover but API just has to be simpler, that’s all.
One publisher/multiple subscribers situation is more common and is quite easy to deal with. I am talking about different case, when there is a need to process signals from different publisher depending which publisher did it come from.
Say, you have one object. And 10 publishers, all using the same callback interface. If your object subscribes to all 10 publishers, it will receive updates, but will not be able to tell which subscriber sent the particular update. Overloading interface methods wont’ work. Adding 10 interfaces is unacceptable. Having 10 proxies will help, but the code may start look bulky.
Using bind and function to create a well know “delegate” pattern does not make code less obvious – static structure is always visible. From the other side, it also helps keeping away the syntax noise, which is inevitably added when the same functional composition is manually created.
Look, Boost::signals and the like just abstract away all the details in Publisher about subscribing you have to deal with now. It’s not much code, of course, but anyway. You still have the same power to subscribe any of the method you like in the observer, just as you do in your example. In fact, with Boost::signals subscribing code would look almost the same, with bind and everything
But as for bind, I’d prefer SubscribeMethod(&subscriber, &MySubscriber::CalledWhenPriceChanges) over your thing with bind. Just because it looks cleaner and is more domain specific. “bind” just an unnecessary detail of implementation, which I’d like to hide away. But I can see how you might prefer the opposite since the API is smaller with it.
well, the bind just helps cleaning up the code a little bit. With it, there is no need to carry class pointers along with method pointers around, etc.
And bind along with function is already a part of the standard and available in the tr1 in VS 2008
That’s exactly the opposite of my point of view, I think ‘bind’ makes code *less* clean. Don’t get me wrong, it’s nice template and it helps tremendously when you deal with containers and other generic classes that need something like a function to be passed as parameter.
But in this specific case, 99.99% of Subscribe calls would be with method, not a function. Making a simple template helper, like
template void SubscribeMethod(T * t, void (*T::f)(T1)) {
Subscribe(boost::bind(f, t, _1));
}
would make all that 99.99% of the calls not only cleaner but also shorter. Less code always means less bugs.
Why do I think it is cleaner *without* bind? Because bind have nothing to do with subscribing, it is there to help C++ achieve something that is so simple and clean looking in functional languages – currying. It is just an implementation detail, something that should not be visible but because language is so restricted we have to use it. It is as annoying as all those ‘function’ keywords you have to use in JavaScript.
And as with ‘function’ keywords, you can’t get rid of bind in C++ application but you can hide it in some specific and wide used cases.
[...] C++ делегаты на основе boost::function (tr1::function) я уже писал. В отличие от стандартного варианта обсервера для C++, [...]