Firing Up C++ Functors – Beautiful Solution to the Problem
This morning I've been battling a really nasty problem in my Greek Engine - at the start of the business day, I need to clear out the previous day's values for some daily summary values - like open/high/low. I do that fine in the instruments themselves, but there's an component in the engine that listens to all trades and creates this summary data. And it wasn't getting cleared out. So when I reloaded all the instruments at the beginning of the day, and flushed all the messages from the exchanges to them, these "lingering" values from the previous day were slipping in.
Ideally, we'd just clear out these values, and I could just clear out the cache in the component. But then I'd loose the last valid trade as well - and that I don't want to loose. So I really to operate on all the messages in the component. This seemed to scream "Iterator!" to me, and I spent several hours on it. The problem is not with the basic iterator concept, it's that we're trying to iterate over a trie, and that's not really all that easy when your trie is lockless, and it's possible for someone to change the data as soon as you're sure it's there.
Yeah, it's nice to think of an iterator on the trie, but it's not really all that practical.
Sure didn't stop me from wasting several hours trying to get it to work, though.
What I really wanted was to be able to pass in some function to operate on all the messages in the trie, and then let the trie itself, handle all the thread-safety issues. This seemed like a much better idea, so I started looking into something I haven't used up till now - functors.
The basic idea is just to have a simple class that has a few standard operators on it, and then use this base class to derive all the operations that you need. For example, in my case what I needed was to operate on Messages. Sure, I only needed to operate on one kind of Message - the Summary Message, but if I'm going to make this, I might as well make it capable of dealing with all the message functor usage I'm going to need.
So I create the following header:
namespace msg { struct MessageFunction { // this is the overt method that can be called bool doIt( Message *aMsg ); // …and this is the simple operator to make it look like a function bool operator()( Message *aMsg ); // these are the methods to override for the different message types virtual bool doHello( message::Hello & aMsg ); virtual bool doGoodBye( message::GoodBye & aMsg ); virtual bool doQuote( message::Quote & aMsg ); virtual bool doPrint( message::Print & aMsg ); }; } // end of namespace msg
and I implemented all the specific message type methods so that if you don't need to implement it, you don't have to, and it'll just be a no-op on the message:
namespace msg { // this is the overt method that can be called bool MessageFunction::doIt( Message *aMsg ) { bool error = false; if (aMsg != NULL) { switch (aMsg->getType()) { case eHello: error = !doHello((message::Hello &)(*aMsg)); break; case eGoodBye: error = !doGoodBye((message::GoodBye &)(*aMsg)); break; case eQuote: error = !doQuote((message::Quote &)(*aMsg)); break; case ePrint: error = !doPrint((message::Print &)(*aMsg)); break; default: error = true; break; } } return !error; } // …and this is the simple operator to make it look like a function bool MessageFunction::operator()( Message *aMsg ) { return doIt(aMsg); } // these are the methods to override for the different message types bool MessageFunction::doHello( message::Hello & aMsg ) { return true; } bool MessageFunction::doGoodBye( message::GoodBye & aMsg ) { return true; } bool MessageFunction::doQuote( message::Quote & aMsg ) { return true; } bool MessageFunction::doPrint( message::Print & aMsg ) { return true; } } // end of namespace msg
Now in my code, I only needed to implement a subclass that did the one thing I needed it to do - reset the values. In this case, I'll use the Quote as an example:
struct QuoteClear : MessageFunction { virtual bool doQuote( message::Quote & aMsg ); };
implemented as:
bool QuoteClear::doQuote( message::Quote & aMsg ) { aMsg.clear(); return true; }
Then all I needed to do was to add in an apply() method to the trie, and have it run through all the values and on each one that is non-NULL, call the functor. The signature for the apply() method is simple, and it makes it clear how to use it:
virtual bool apply( MessageFunction & aFunctor );
This was an excellent use of the idea, as it allowed me to pass in an arbitrarily complex function to be operated on a general Message, and the apply() method simply knows how to scan it's internal data structure, and call this application as needed. Very sweet.
While it's not as generally useful as an iterator on a trie, it's perfectly suited to what I needed. Love the idea.