
Frequently I have to write comparison functions (usually of the "less" variety) that involve multiple keys or members of a struct. For example: struct s { A a; B b; C c; D d; // there may be other members not used in the comparison }; // operator< function or an equivalent function object bool operator<( const s& lhs, const s& rhs ) { if( lhs.a < rhs.a ) return(true); else if( lhs.a == rhs.a ) { if( lhs.b < rhs.b ) return(true); else if( lhs.b == rhs.b ) //and so on... } return(false); } This gets quite repetitive, especially if I have to write alternate sort orders (e.g. b,a,c,d or c,d,a,b) I'm thinking there has to an easier way to do this or a way to do a generic algorithm for this. Is there anything in boost that would help? I had an idea that one might be able to fill a vector with binders for each element and then call lexicographical_compare, but I'm not sure how to make it work.

AMDG Bill Buklis wrote:
Boost.Fusion provides several ways to do this. #include <boost/fusion/include/less.hpp> #include <boost/fusion/include/adapt_struct.hpp> BOOST_FUSION_ADAPT_STRUCT(s, (A, a)(B, b)(C, c)(D, d)) bool operator<( const s& lhs, const s& rhs ) { return boost::fusion::less(lhs, rhs); } If you need different sort orders, you can use tie: #include <boost/fusion/include/vector_tie.hpp> bool operator<( const s& lhs, const s& rhs ) { return boost::fusion::vector_tie(lhs.a, lhs.b, lhs.c, lhs.d) < boost::fusion::vector_tie(rhs.a, rhs.b, rhs.c, rhs.d); } In Christ, Steven Watanabe

AMDG Bill Buklis wrote:
If one of the members was a C-style string, how can I override the comparison to use strcmp or equivalent?
It would be easy if you could pass a predicate to less. Unfortunately Boost.Fusion doesn't provide such an overload. I've adapted the implementation of less. See attached. In Christ, Steven Watanabe

Thanks very much for taking the time for that adaptation. Unfortunately a lot of these strings are actually char arrays rather than pointers (way too much legacy code left). I couldn't figure out how to make BOOST_FUSION_ADAPT_STRUCT recognize the types (one of those array vs pointer differences), but then I realized it doesn't really matter for this case. I can make this work using vector_tie and a simple wrapper. It's nowhere near as elegant, but it seems to work. Any thoughts on potential problems that I might be missing? See below: struct char_wrapper { const char * str; char_wrapper( const char* str ) : str(str) {} bool operator<( const char_wrapper& rhs ) const { return(strcmp(str, rhs.str) < 0); } }; struct s { int a, b; char c[20]; }; bool operator<( const s& lhs, const s& rhs ) { // sort in order of b,a,c return( boost::fusion::vector_tie(lhs.b, lhs.a, char_wrapper(lhs.c)) < boost::fusion::vector_tie(rhs.b, rhs.a, char_wrapper(rhs.c)) ); } -- Bill --

AMDG Bill Buklis wrote:
It seems to be fine as long as you use a typedef. // doesn't compile struct S { char x[10]; }; BOOST_FUSION_ADAPT_STRUCT(S, (char[10], x)); // okay typedef char x_type[10]; struct S { char x[10]; }; BOOST_FUSION_ADAPT_STRUCT(S, (x_type, x));
You can't pass a non-const rvalue to vector_tie. In Christ, Steven Watanabe
participants (3)
-
Bill Buklis
-
Christopher Jefferson
-
Steven Watanabe