Question

Basically, I am implementing a container class. I need to create a method which sorts the data according to the comparator function/functor which must be passed with the sort request. Since declaration and definition of the method are in different files(.h and .cpp) using templates becomes a problem (and I don't have much experience using them). In other words, I want to make a method:

void sort(function/functor f);

and I don't know how to define the function. So, is there any solution to this problem?

Was it helpful?

Solution

If you know the signature of the function/functor to be passed[*], you can use std::function. Or boost::function if you don't have C++11. So for a comparator it would be:

void sort(std::function<bool(const Element&, const Element&)> f);

where Element is the type of the elements of the container.

Failing that you could define a class with a virtual compare function, and allow callers to derive from it with their own class. It's more work for callers, but that's exactly what std::function provides: a way for callers to not have to do that.

Two warnings:

  • make sure there's really a good reason for the implementation of your container not to be in the header file. All of the standard containers are implemented in header files, and that mostly works OK.

  • make sure there's really a good reason for you to implement a sort function. Standard containers only have sort functions where std::sort doesn't work: std::list and std::forward_list.

[*] Actually std::function doesn't require the exact same signature. You just say the types you want to call it with and the type you want to convert the return value to. So if you call it with two ints and the caller provides a function that takes two longs, then that's fine. The arguments are converted just like the function call would without any std::function.

OTHER TIPS

This is generally done with templates. Like this:

#include <iostream> // For example output only.

template <typename F>
void sort(F&& pred) {
    pred(123);
}

void normal_func(int v) {
    std::cout << "normal_func(" << v << ")\n";
}

struct my_pred {
    void operator()(int v) const {
        std::cout << "my_pred(" << v << ")\n";
    }
};

int main() {
    sort([](int v) { std::cout << "Called lambda func with " << v << '\n'; });
    sort(normal_func);
    sort(my_pred());
}

If, however, templates cannot be used, then your best bet would be to use polymorphic function wrapper like std::function (or boost::function, or you can write your own simple version).

Alternatively, you can use a hardcore C-style regular function along with a void pointer where user can save their context. For example, like qsort(). Though I'd try not to go there if possible.

Here is an example using std::function:

#include <iostream> // For example output only.
#include <functional> // For std::function.

void sort(const std::function<void(int)>& pred) {
    pred(123);
}

void normal_func(int v) {
    std::cout << "normal_func(" << v << ")\n";
}

struct my_pred {
    void operator()(int v) const {
        std::cout << "my_pred(" << v << ")\n";
    }
};

int main() {
    sort([](int v) { std::cout << "Called lambda func with " << v << '\n'; });
    sort(normal_func);
    sort(my_pred());
}

Hope it helps.

You can use std::function as suggested in Steve Jessop's answer but in this case I think you should consider making your sort function a template function as in Vlad's answer. A sort function has to call the comparator many times and there will be noticeable overhead to using std::function in this situation.

The easiest solution is to use a template:

  class C
  {
      template<typename T>
      void sort(T func)
      {
            func(12,45);   // Will compile as long as your function/functor
      }                    // Can take two integers as parameters.
  };                       // NOTE: or integers can be converted into your parameters.

If you want to specify using old C notation.

  typedef void (*FUNC_TYPE)(int, int);  // declares a function pointer type.
                                        // returns void takes two integers.
  class C
  {
      void sort(FUNC_TYPE func)
      {
          func(12,45);                      // Compiles only if the function
      }                                     // matches the exact type.
  };

The C++11 way

  class C
  {
      void sort(std::function<void(int,int)> func)
      {
          func(12,45);                     // Will match any func/functor that
                                           // will return a void and takes two
      }                                    // integers.
  };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top