2

I have a std::list that I would like to sort with a comparator chosen from a set. I would like to use boost bind to define the comparator, so that I can implicitly define a function for each comparator. Something to the effect of:

struct MyStruct { int a; int b };
std::list<MyStruct> myList;
...
myList.sort(_1.a < _2.a);

The above code does not compile. My question is, how can I use boost to define comparators inline?

MM.
  • 4,224
  • 5
  • 37
  • 74
  • Just curious... do you have a C++11 compiler? Any reason you don't want to use a lambda? – Tony Delroy Apr 14 '14 at 23:44
  • No c++11 for me, unfortunately :( – MM. Apr 14 '14 at 23:45
  • Try `myList.sort(bind(&MyStruct::a, _1) < bind(&MyStruct::b, _2))`. That usage is documented [here](http://www.boost.org/doc/libs/1_55_0/libs/bind/bind.html). – Tony Delroy Apr 15 '14 at 01:12
  • 1
    Are you sure about your ordering? `_1.a < _2.b` is not a well defined strict weak ordering. Example: `(0,1)` is both smaller and bigger than `(0,2)`... – Danvil Apr 15 '14 at 08:14

1 Answers1

2

I'd use Boost Phoenix:

#include <boost/phoenix.hpp>
#include <list>

namespace phx = boost::phoenix;
using namespace phx::arg_names;

struct MyStruct { int a; int b; };

int main()
{
    std::list<MyStruct> myList;
    //...
    myList.sort(phx::bind(&MyStruct::a, arg1) < phx::bind(&MyStruct::b, arg2));
}

Note that it seems extremely weird to be comparing different fields (unless the fields have some guaranteed redundancy relation (e.g.: they are always equal) it will not satisfy the requirements for a Strict Weak Total Ordering - required for most STL containers/algorithms that take a comparer.

To avoid both

  • the verbosity of the comparator, as well as
  • the risk of having different accesors on the left-hand/right-hand sides

I usually use a helper (c++03):

#include <boost/bind.hpp>
#include <list>

template <typename F>
struct compare_by_impl {
    compare_by_impl(F f = F()) : _f(f) {}

    template <typename T, typename U>
    bool operator()(T const& a, U const& b) const {
        return _f(a) < _f(b);
    }
  private:
    F _f;
};

template <typename Accessor>
compare_by_impl<Accessor> comparer_by(Accessor f) {
    return compare_by_impl<Accessor>(f);
}

struct MyStruct { int a; int b; };

int main()
{
    std::list<MyStruct> myList;
    //...
    myList.sort(comparer_by(boost::mem_fn(&MyStruct::a)));
}

This no longer uses Boost Phoenix. See it Live on Coliru.

See a more up-to-date c++11 version here: How to implement a lambda function for a sort algorithm involving object members, indirection, and casting?

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • @Danvil Huh? it's not that much, really. I use a facility like compare_by_impl in c++11 too, it's not related to the compiler support of c++11 features. And it's much less verbose than a c++11 lambda. Also, note that Boost Phoenix/Lambda expressions are polymorphic. Try that with c++11 lambdas... – sehe Apr 15 '14 at 08:07
  • I did not mean offence. Nice answer! Just thinking that `list.sort([](const MyStruct& a, const MyStruct& b) { return a.a < b.b; });` looks cleaner. – Danvil Apr 15 '14 at 08:11
  • @Danvil honestly, I'd not let that pass code review. I agree that it's the OP's code, but it seems broken. And for this exact reason I'd require a version that is more DRY (no need to repeat `.a` dereferencing or `const MyStruct&`). Lambdas, while succinct, do still have a mental cost while reading for me. – sehe Apr 15 '14 at 08:27
  • My apologies. The original comparison was typo'd. I did not intentionally compare two different fields. – MM. Apr 15 '14 at 16:06
  • @MM. Cheers :/ No problem. It actually underlines why you'd want to avoid the repetition in your code. Less code less bugs, code duplication: playing with fire. – sehe Apr 15 '14 at 19:03