C++ tricks: lazy mans operator<

Disclaimer: This may seem less interesting when its not 2am.

So Im writing this very simple class representing market data subscriptions.
Its really just an aggregate of some data.
Session id, symbol to subscribe to, and subscription request id.

It basically looks a little bit like

class Subscription {
   public:
      // ...
   private:
      FIX::SessionID session;
      std::string symbol;
      std::string req_id;
};

At some point I wanted to put these in an STL set.
This means I need a function that is a model of Strict Weak Ordering

All the members already have operator< but its still tedious to write a conforming func when theres more than a few members. Typically you end up with things like

inline bool lt_compare(const Subscription& a, const Subscription& b) {
   return a.get_session() < b.get_session()
      || a.get_session() == b.get_session() && a.get_symbol() < b.get_symbol()
      || a.get_session() == b.get_session() && a.get_symbol() == b.get_symbol() && a.get_req_id() < b.get_req_id();
}

Which isnt pretty and is easy to make a typo in. Adding more members just makes it uglier.

So I started thinking how Id do it in Python and came up with something like

def lt_compare(a,b):
   return (a.session,a.symbol,a.req_id) < (b.session,b.symbol,b.req_id)

This works because python’s tuples support __lt__ in the obvious manner.

I also recalled that std::pair provides operator<. Seeing as boost::tuple is an extension of std::pair I realized I could leverage that.

inline bool lt_compare (const Subscription& s1, const Subscription& s2) {
	return boost::make_tuple(s1.get_session_id(), s1.get_symbol(), s1.get_req_id())
		< boost::make_tuple(s2.get_session_id(), s2.get_symbol(), s2.get_req_id());
}

Quite close to the python version. IMO less prone to typos than the original C++ version.

Now for the problem: It does copy the members when it makes the tuple. This means its still not suitable for classes with heavy copy constructors. Like most versions of string.
We can fix that by wrapping all the arguments in boost::cref.

inline bool lt_compare (const Subscription& s1, const Subscription& s2) {
	return boost::make_tuple( boost::cref(s1.get_session_id()), boost::cref(s1.get_symbol()), boost::cref(s1.get_req_id()))
		< boost::make_tuple( boost::cref(s2.get_session_id()), boost::cref(s2.get_symbol()), boost::cref(s2.get_req_id()));
}

but it starts to look ugly again.
In the end I didnt use it anyway. I didnt need to store them in a set after all.

It should be pretty easy to write a template or macro thats even easier to use.
For 1 mins work i thought it was a neat trick.

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>