views:

351

answers:

3

Having seen the advantages of metaprogramming in Ruby and Python, but being bound to lower-level languages like C++ and C for actual work, I'm thinking of manners by which to combine the two. One instance comes in the simple problem for sorting lists of arbitrary structures/classes. For instance:

struct s{
  int a;
  int b;
};

vector<s> vec;
for(int x=0;x<10;x++){
  s inst;
  inst.a = x;
  inst.b = x+10;
  vec.push_back(inst);
}

Ultimately, I'd like to be able to sort inst arbitrarily with a minimal amount of boilerplate code. The easiest way I can see to do this is to make use of STL's sort:

sort(vec.begin(),vec.end());

Yet this requires me to write a method that can compare "struct s"s. What I'd rather do is:

sort(vec,a ASC,b DESC);

Which is very clearly not valid C++.

So, SO community. What is the best way to accomplish my dream? If I had some sort of typeful macro, that would reveal to me what the type of a vector's elements were, then it would be trivial to write C preprocessor macros to create the function required to do the sorting.

The alternative seems to be to write my own preprocessor. This works well, up until the point where I have to deduce the type of "vec" again. Is there an easy way to do this?

Context: Less code = less bugs, programming competitions.

A: 

For c++ the standard library offers the algorithms header which contains many useful functions that work on various containers. An example for your purposes would be:

bool sCompare(const s & s1, const s & s2) {
return s1.a+s1.b/1000 < s2/a+s2.b/1000;
}

vector<s> vec;
...
std::sort(vec.begin(), vec.end(), sCompare);

sort has a prototype that looks something like:

template<class Iter, class Op>
void sort(Iter& start, Iter& stop, Op&  op);

Most of these algorithms should work for any of the standard containers (some are specific to sorted containers, some associative, etc). I believe sort (and others) will even work with arrays (Iterators, the foundation of algorithms, are built to emulate pointers to array elements as closely as possible.)

In short, using modern c++ you will not need a special preprocessor to achieve what you're trying to do.

BTW, if you've declared that you're using std or std::sort, then sort(vec.begin(),vec.end()) is valid c++;

KitsuneYMG
The /1000 is clearly wrong here
e.tadeu
+3  A: 

For the above, you can use Boost.Lambda to write your comparison function inline, just like a Python lambda:

using namespace boost::lambda;

std::sort(vec.begin(), vec.end(), (_1 ->* &s::a) < (_2 ->* &s::a));

This of course assumes that you are sorting by a.

If the expressions you are looking for are far more complex, you are better off writing a separate function; even in languages like Python and Ruby with native support for closures, complex closures become quite unreadable anyway.

Warning: The code above is untested.

Hope this helps!

blwy10
Brilliant, I knew someone had to have done this with templates. Thanks!
Stefan Mai
+2  A: 

I would stick with writing a comparison operator for the struct. The bonus of having a comparison operator defined is that you don't end up with multiple lambda comparisons scattered all over the place. Chances are that you will need a comparison operator more than just once, so why not define it once in the logical place (along with the type)?

Personally, I prefer writing code once and keeping it some place that is particularly easy to find. I also favor writing code that is idiomatic with respect to the language that I am writing in. In C++, I expect constructors, destructors, less-than operators, and the like. You are better off writing a less-than operator and then letting std::sort(vec.begin(), vec.end()) do its proper job. If you really want to make your code clear, then do something like:

struct S {
    int a, b;
    bool less_than(S const& other) {...};
};
bool operator<(S const& left, S const& right) {
    return left.less_than(right);
}

If you define a member function to do the comparison and then provide the operator at the namespace-level, life is much easier when you have to negate the comparison. For example:

void foo(std::vector<S>& svec) {
    std::sort(svec.begin(), svec.end(), std::not1(&S::less_than));
}

This code is untested but you get the idea.

D.Shawley