tags:

views:

81

answers:

3

I have a fairly large, sophisticated algorithm that uses a std::priority_queue. In some cases, I want the queue to be a min-queue, and in others, a max-queue. So I have to provide a comparator type that does one or the other. Since the comparator is a template parameter, it is fundamentally part of the type of the std::priority_queue. So everywhere the queue is referenced, the code must know the type.

One option, of course, is to provide a custom comparator that has state, and selects between ascending and descending on each invocation of operator(), but I'm trying to avoid the performance overhead of having this branch for every comparison. The other alternative that I can see is to duplicate the entire algorithm, one for sorting ascending, and the other for descending.

Is there a better alternative to this problem?

+3  A: 

Use templates

template <typename Comparator>
void algo()
{
  std::priority_queue<int, std::vector<int>, Comparator> pqueue;
  ...
}
Ugo
+1  A: 

If you want to use a comparator which has state then you can not change that state because the comparator is copied by the queue. In order to change the state of your comparator after your queue was constructed you can use shared state.

However the state should not be used to alter the result of comparisons after elements are added to the queue. This would probably cause weird behavior of the queue.

struct shared_state_t
{
 int number;
};

struct compare_t
{
 bool operator <(const record_t &left, const record_t &right) const { left < right; }
 tr1::shared_ptr<shared_state_t> shared;
};

void func()
{
 tr1::shared_ptr<shared_state_t> state(new shared_state_t());
 state->number = 42;
 compare_t comp;
 comp.shared = state;
 std::priority_queue<record_t, vector<record_t>, compare_t> queue(comp);
 // do something with queue
 state->number = 999;
 // compare object of queue is aware of the new state
}
frast
Sorry, I didn't mean to imply that I'm changing state during the lifetime of the queue -- I am not. It's on each invocation of the algorithm.
Tabber33
@Tabber: then why would you need to branch at all? Just create a `priority_queue<T, min_comparator>` and a `priority_queue<T, max_comparator>`, and use whichever does what you need.
jalf
But then I have to check a flag before each access to the queue, in addition to creating two queue objects, when I only need one. I guess I just have to templatize the whole algorithm on the stinking comparator class... this is the only way to avoid *many costly runtime checks* to see which direction I'm currently sorting.
Tabber33
A: 

std::priority_queue takes a comparator object in its constructor. Take the object as a function parameter and pass it to the constructor. Each call will create a different container depending on the parameter. So the branch is only at the time of construction.

template<class C>
void the_algorithm(const C& compare)
{
    std::priority_queue<Type> q(compare);
    // ...
}

bool this_before_that(const Type& op1, const Type& op2);

// ...
{
    the_algorithm(this_before_that);
    the_algorithm(ThatBeforeThis_Functor());
}

The branching count here is merely in the order of calls to the algorithm.

wilhelmtell
Not true. Try to code this, and I think you'll see why this is wrong. The *type* of the comparator must be the same. So the branch must be inside the comparator function.
Tabber33
@Tabber33 the type changes depending on the comparator, yes. But I understood you don't need to share the same container for difference comparison modes. Have the algorithm work with a single type of container depending on how it was called. what's wrong here?
wilhelmtell
I don't understand what you are suggeting here... could you show some sample code? `...a single type of container depending on how it was called` sounds like you are suggeting the algorithm function being a template with the comparison object type a template parameter?
Tabber33
@Tabber33 yes. and now i see that someone has already suggested exactly that. only thing is, if you take the object as a parameter then it's somewhat easier to invoke the algorithm with primitive functions.
wilhelmtell
Indeed, but that doesn't solve my problem, which is getting the check out of the comparison function.
Tabber33
I think I just figured out how to do this... by using a function instead of a functor. I can declare the template parameter as a pointer to function and pass a different pointer to the queue constructor... If you post another answer with that suggestion, I can accept it.
Tabber33
@Tabber33, if you have a solution that isn't represented by any other answers, you can answer your own question.
Mark Ransom