views:

341

answers:

5

Consider the class:

MyClass {
    int varA;
    int varB;
};

I have a vector of pointers to MyClass objects:

std::vector<MyClass*> Vec;

I want to sort the vector according to varA or varB using the same sort function, i.e. :

bool SortFunction(const MyClass* obj1, const MyClass* obj2, const short type) {
     if( type == VARA_ID )
         return obj1->varA < obj2->varA;
     else if( type == VARB_ID )
         return obj1->varB < obj2->varB;
}

AFAICT this is not possible. What would be the most elegant way to this without using external libraries?

+2  A: 

Does the value of type change with each comparison? It cannot, as it appears. In that case, use a curried function -- see boost::bind.

std::sort(v.begin(), v.end(), boost::bind(SortFunction, _1, _2, type));
dirkgently
Don't you mean `bind(SortFunction, _1, _2, type)`?
Chris Jester-Young
Yes, too lazy ...
dirkgently
+10  A: 
class sorter {
      short type_;
public:
      sorter(short type) : type_(type) {}
      bool operator()(MyClass const* o1, MyClass const* o2) const {
            return SortFunction(o1, o2, type_ );
      }
};

std::sort(Vec.begin(), Vec.end(), sorter(MY_TYPE) );
OwnWaterloo
+1 Very good, you beat me to my answer. I'd personally make `sorter` derive from `std::binary_function<MyClass const*, MyClass const*, bool>`, but that's optional here.
Chris Jester-Young
Yes, nested typedefs is required only by std::bind1st or std::bind2nd, so I omitted it ^_^.
OwnWaterloo
Perfect. Thanks.
RegularJack
Actually, I'd make `type` a _template_ parameter, passing it as `std::sort(..., sorter<MY_TYPE>())`. That eliminates O(N log N) run-time checks.
MSalters
@MSaltersYes, your solution is more suitable to RegularJack's requirement~
OwnWaterloo
+2  A: 

A more generic solution might also be to make use of pointers-to-members:

#include <vector>
#include <algorithm>
#include <functional>

struct MyClass {
    int varA;
    int varB;
};

template <class Object, class VarType>
class CompareMemberT: public std::binary_function<bool, const Object*, const Object*>
{
    VarType Object::*p;
public:
    CompareMemberT(VarType Object::*p): p(p) {}
    bool operator()(const Object* a, const Object* b) const
    {
        return a->*p < b->*p;
    }
};

//helper to deduce template arguments
template <class Object, class VarType>
CompareMemberT<Object, VarType> CompareMember(VarType Object::*p)
{
    return CompareMemberT<Object, VarType>(p);
}

int main()
{
    std::vector<MyClass*> vec;
    std::sort(vec.begin(), vec.end(), CompareMember(&MyClass::varA));
    std::sort(vec.begin(), vec.end(), CompareMember(&MyClass::varB));
}
UncleBens
+3  A: 

You're almost there, make type a template parameter and the signature is OK:

template<int type>
bool SortFunction(const MyClass* obj1, const MyClass* obj2) {
     if( type == VARA_ID )
         return obj1->varA < obj2->varA;
     else // if( type == VARB_ID ) -- A sort function must have a default.
         return obj1->varB < obj2->varB;
}

std::sort(Vec.begin(), Vec.end(), &SortFunction<VARA_ID> );

The optimizer will spot that ( type == VARA_ID ) is a compile-time constant.

MSalters
+1 for the idea, though I think you meant to say `template <short type>` rather than `template <typename type>`.
Chris Jester-Young
Oops, yes. Fixed.
MSalters
+3  A: 

Using Boost.Lambda and without any special sorting function, right in your code where you need sorting:

Briefly

// sort with VARA_ID
sort(Vec.begin(), Vec.end(), bind(&MyClass::varA, _1)<bind(&MyClass::varA, _2));

// sort with VARB_ID
sort(Vec.begin(), Vec.end(), bind(&MyClass::varB, _1)<bind(&MyClass::varB, _2));

Full example

#include <iostream>
#include <vector>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <algorithm>

using namespace boost::lambda;

class MyClass {
public:
    int varA;
    int varB;
};

enum SorterType {
    VARA_ID,
    VARB_ID 
};

int main()
{

    std::vector<MyClass*> Vec;

    Vec.push_back(new MyClass());
    Vec.push_back(new MyClass());
    Vec.push_back(new MyClass());

    Vec[0]->varA = 1; Vec[0]->varB = 8;
    Vec[1]->varA = 2; Vec[1]->varB = 7;
    Vec[2]->varA = 3; Vec[2]->varB = 6;

    // sort with VARA_ID
    std::sort(Vec.begin(), Vec.end(), 
              bind(&MyClass::varA, _1)  < bind(&MyClass::varA, _2) );

    // VARB_ID
    std::sort(Vec.begin(), Vec.end(), 
              bind(&MyClass::varB, _1)  < bind(&MyClass::varB, _2) );

    return 0;
}
skwllsp