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:
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.
Boost.Fusion provides several ways to do this.
#include
-----Original Message----- From: Steven Watanabe [mailto:watanabesj@gmail.com] Sent: Thursday, August 27, 2009 1:43 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] Generically comparing structs
AMDG
Bill Buklis wrote:
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.
Boost.Fusion provides several ways to do this.
#include
#include 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
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
Oh, that is sweet. Thanks. I knew there had to be something.
-----Original Message----- From: Steven Watanabe [mailto:watanabesj@gmail.com] Sent: Thursday, August 27, 2009 1:43 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] Generically comparing structs
AMDG
Bill Buklis wrote:
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.
Boost.Fusion provides several ways to do this.
#include
#include 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
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
OK, one more related question: If one of the members was a C-style string, how can I override the comparison to use strcmp or equivalent? -- Bill --
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
-----Original Message----- From: Steven Watanabe [mailto:watanabesj@gmail.com] Sent: Thursday, August 27, 2009 4:57 PM To: boost-users@lists.boost.org Subject: Re: [Boost-users] Generically comparing structs
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:
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.
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));
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)) ); }
You can't pass a non-const rvalue to vector_tie. In Christ, Steven Watanabe
On 27 Aug 2009, at 19:42, Steven Watanabe wrote:
AMDG
Bill Buklis wrote:
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.
Boost.Fusion provides several ways to do this.
#include
#include 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
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); }
If you don't want to pull in fusion, then tuple::tie will perform exactly the same task (this is what I usually use). Chris
participants (3)
-
Bill Buklis
-
Christopher Jefferson
-
Steven Watanabe