Comparison function to sort()

D

David Rasmussen

I want to use sort() and supply my own comparison function, like

bool lessThan(const S& a, const S& b)
{
return value(a) < value(b);
}

and then sort by:

sort(a.begin(), a.end(), lessThan);

All trivial stuff, but I have several different metrics or value()
functions if you will. I could copy and paste 100 times so I get 100
functions like:

bool lessThan42(const S& a, const S& b)
{
return value42(a) < value42(b);
}

but isn't there an easier way? Can't I make lessThan a template function
that takes the metric (value() function) as a template argument or some
other smart thing that will save me from all that copying and pasting?

/David
 
M

Markus Moll

Hi

David said:
All trivial stuff, but I have several different metrics or value()
functions if you will. I could copy and paste 100 times so I get 100
functions like:

bool lessThan42(const S& a, const S& b)
{
return value42(a) < value42(b);
}

but isn't there an easier way? Can't I make lessThan a template function
that takes the metric (value() function) as a template argument or some
other smart thing that will save me from all that copying and pasting?

Indeed, one possibility is

template<typename Acc>
struct my_less_cmp
{
Acc acc;
my_less_cmp(Acc acc = Acc())
: acc(acc)
{}

bool operator()(const S& a, const S& b)
{
return acc(a) < acc(b);
}
};

[...]

std::sort(a.begin(), a.end(), my_less_cmp<some_accessor>());

Markus
 
D

David Rasmussen

Markus said:
Indeed, one possibility is

template<typename Acc>
struct my_less_cmp
{
Acc acc;
my_less_cmp(Acc acc = Acc())
: acc(acc)
{}

bool operator()(const S& a, const S& b)
{
return acc(a) < acc(b);
}
};

[...]

std::sort(a.begin(), a.end(), my_less_cmp<some_accessor>());

Okay, but then Acc/some_accessor will have to be a functor as well,
right? I mean, I can't just pass a function (pointer) as template argument?

/David
 
I

int2str

David said:
but isn't there an easier way? Can't I make lessThan a template function
that takes the metric (value() function) as a template argument or some
other smart thing that will save me from all that copying and pasting?

In addition to Markus's template solution, maybe a functor object may
help as well.

Something similar to this:

class test
{
public:
enum direction
{
ASCENDING,
DESCENDING
};

test( direction d = ASCENDING )
: d_( d )
{
}

bool operator() ( const int & a, const int & b )
{
if ( d_ == DESCENDING )
return a > b;

return a < b;
}

private:
direction d_;
};

....

sort( iv.begin(), iv.end(), test( test::DESCENDING ) );

Cheers,
Andre
 
D

David Rasmussen

Markus said:
Indeed, one possibility is

template<typename Acc>
struct my_less_cmp
{
Acc acc;
my_less_cmp(Acc acc = Acc())
: acc(acc)
{}

bool operator()(const S& a, const S& b)
{
return acc(a) < acc(b);
}
};

[...]

std::sort(a.begin(), a.end(), my_less_cmp<some_accessor>());

Hmm. I don't understand how this could work.

My value functions are all of the form:

double value42(const S& s)
{
return bla bla bla;
}

double value67(const S& s)
{
return something else bla bla bla something else;
}

With your solution, I can't write:

std::sort(a.begin(), a.end(), my_less_cmp<value42>());

which is kind of what I want to do?

/David
 
M

Markus Moll

Hi

David said:
Okay, but then Acc/some_accessor will have to be a functor as well,
right? I mean, I can't just pass a function (pointer) as template
argument?

Yes, you can. (Well, not exactly pass it as a template argument...)

int some_func(const S& s)
{
return s.x;
}

std::sort(a.begin(), a.end(), my_less_cmp<int (*)(const S&)>(some_func));

Markus
 
J

Jack Saalweachter

David said:
Markus said:
Indeed, one possibility is

template<typename Acc>
struct my_less_cmp
{
Acc acc;
my_less_cmp(Acc acc = Acc()) : acc(acc)
{}

bool operator()(const S& a, const S& b)
{
return acc(a) < acc(b);
}
};

[...]

std::sort(a.begin(), a.end(), my_less_cmp<some_accessor>());

Hmm. I don't understand how this could work.

My value functions are all of the form:

double value42(const S& s)
{
return bla bla bla;
}

double value67(const S& s)
{
return something else bla bla bla something else;
}

With your solution, I can't write:

std::sort(a.begin(), a.end(), my_less_cmp<value42>());

which is kind of what I want to do?

/David
You can template on a function pointer, yes. That would go something like:
template <double (*fxn)(const int)>
bool compare_function(const int a, const int b) {
return (*fxn)(a) < (*fxn)(b);
}

If you want to template on the parameter to the function, you'll
probably have to say "template <typename T, double (*fxn)(T)>", and be
doomed to pass it the parameter type as well.
 
R

Rolf Magnus

David Rasmussen wrote:

Okay, but then Acc/some_accessor will have to be a functor as well,
right? I mean, I can't just pass a function (pointer) as template
argument?

Yes, you can. But a function object is usually faster.
 
D

David Rasmussen

Rolf said:
Because the function is called directly instead of through a pointer. The
compilers that I know never inline functions called through a pointer. If
the comparison function is very short (and comparison functions tend to be
short), inlining can make a big difference.

I suspected an answer along those lines. Thanks.

/David
 
R

Rolf Magnus

David said:
How come?

Because the function is called directly instead of through a pointer. The
compilers that I know never inline functions called through a pointer. If
the comparison function is very short (and comparison functions tend to be
short), inlining can make a big difference.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,054
Latest member
LucyCarper

Latest Threads

Top