tags:

views:

1115

answers:

4

Template methods as in NOT C++ templates.

So, say that you would like to do some searching with different algorithms - Linear and Binary for instance. And you would also like to run those searches through some common routines so that you could, for instance, automatically record the time that a given search took and so on.

The template method pattern fills the bill beautifully. The only problem is that as far as I've managed to dig around, you can't actually implement this behaviour via static methods with C++, 'cause you would also need to make the methods virtual(?) Which is of course a bit of a bummer because I don't have any need to alter the state of the search object. I would just like to pin all the searching-thingies to its own namespace.

So the question is: Would one want to use something like function/method pointers instead? Or would one just use namespaces to do the job?

It's pretty hard to live with this kind of (dare I say) limitations with C++, as something like this would be a breeze with Java.

Edit:

Oh yeah, and since this is a school assignment, the use of external libraries (other than STL) isn't really an option. Sorry for the hassle.

+2  A: 

Here's a simple templatized version that would work

template <typename F, typename C>
clock_t timer(F f, C c)
{
    clock_t begin = clock();
    f(c);
    return clock() - begin;
}

void mySort(std::vector<int> vec)
{ std::sort(vec.begin(), vec.end()); }

int main()
{
    std::vector<int> vec;
    std::cout << timer(mySort, vec) << std::endl;
    return 0;
}
Matt Price
A: 

You can always implement it the same way you would in Java - pass in an abstract class ISearchable that has a search() method, and override that in LinearSearch and BinarySearch...

You can also use a function pointer (which would be my preffered solution) or a boost::function, or templatize your function and pass in a functor.

Greg Rogers
+1  A: 

static doesn't say "I don't need alter an object's state" is says, "I don't need an object". If you need virtual dispatch then you need an object on which to perform virtual dispatch as virtual dispatch is polymorphism based on the runtime type of an object. const would be "I don't need to alter an object's state" and you can have methods which are virtual and const.

Charles Bailey
Okay, but i wouldn't actually need an object either..
JHollanti
If you need virtual functions, you automatically need an object. ;)
jalf
Yei!, learned something new today :)
JHollanti
+5  A: 

I don't see why you'd need the template method pattern.

Why not just define those algorithms as functors that can be passed to your benchmarking function?

struct BinarySearch { // functor implementing a specific search algorithm
  template <typename iter_type>
  void operator()(iter_type first, iter_type last){ ....}
};

template <typename data_type, typename search_type>
void BenchmarkSearch(data_type& data, search_type search){ // general benchmarking/bookkeeping function
// init timer
search(data);
// compute elapsed time
}

and then call it like this:

int main(){
  std::vector<int> vec;
  vec.push_back(43);
  vec.push_back(2);
  vec.push_back(8);
  vec.push_back(13);

  BenchmarkSearch(vec, BinarySearch());
  BenchmarkSearch(vec, LinearSearch()); // assuming more search algorithms are defined
  BenchmarkSearch(vec, SomeOtherSearch();
}

Of course, another approach, which is a bit closer to what you initially wanted, could be to use CRTP (A pretty clever pattern for emulating virtual functions at compile-time - and it works with static methods too):

template <typename T>
struct SearchBase { // base class implementing general bookkeeping
  static void Search() {
    // do general bookkeeping, initialize timers, whatever you need
    T::Search(); // Call derived search function
    // Wrap up, do whatever bookkeeping is left
  }
}


struct LinearSearch : SearchBase<LinearSearch> // derived class implementing the specific search algorithms
{
  static void Search(){
    // Perform the actual search
  }
};

Then you can call the static functions:

SearchBase<LinearSearch>::Search();
SearchBase<BinarySearch>::Search();
SearchBase<SomeOtherSearch>::Search();

As a final note, it might be worth mentioning that both of these approaches should carry zero overhead. Unlike anything involving virtual functions, the compiler is fully aware of which functions are called here, and can and will inline them, resulting in code that is just as efficient as if you'd hand-coded each case.

jalf
+1 for typing faster than me.
Jeff Leonard
So that would be to define it in its own namespace?
JHollanti
@JHollanti: Not sure what you mean. You could implement it all in the same namespace, or each algorithm in a separate namespace, that's not really relevant to my example. The point is that your search algorithm is an object that is passed to the surrounding function, which can then do all the benchmarking and other bookkeeping in addition to invoking the algorithm.
jalf
@jalf: Yeah, got it, thanks. I know what you mean and it's a good solution. The reason i wanted to do it with objects was so that i could pack it all to a nice wrapping. And okay, i wanted to impress the teacher as well ;)
JHollanti
I don't see how that would be a nicer wrapping. I can't imagine it getting much nicer than this approach (feel free to put it in a namespace to group it together nicely, of course) --- apart from that, I think most teachers would be much more impressed by functors than virtual functions. :)
jalf
Functors, that's the second thing you managed to teach me today. Cheers!
JHollanti
Here's a third. Just updated my question. :)
jalf
Now *this* is exactly what i was after for. Perfect, thanks mate! Altough at this point i might actually go for the functors, if only 'cause i've never heard or used them before. Still, i appreciate the trouble you saw regarding the CRTP.
JHollanti
Yeah, in this case I'd prefer the functor approach too. It gives you more flexibility, (both the benchmark function and the search functors can be used individually, or with other functions/functors. There doesn't seem to be much gained by using CRTP in this case.
jalf
CRTP binds it to OO quite nicely though. Which is - atleast for me - always a nice thing.
JHollanti