8

Say I have some code:

void barA() { }
void barB() { }

void fooA() {
  // Duplicate code...
  barA();
  // More duplicate code...
}

void fooB() {
  // Duplicate code...
  barB();
  // More duplicate code...
}

int main() {
  fooA();
  fooB();
}

And I want to remove the duplicate code between fooA and fooB I could use a number of dynamic techniques such as passing in a bool parameter, passing a function pointer or virtual methods but if I wanted a compile time technique I could do something like this:

struct A { };
struct B { };

template<typename Tag> void bar();
template<> void bar<A>() { }
template<> void bar<B>() { }

template<typename Tag> void foo() {
  // Duplicate code
  bar<Tag>();
  // More duplicate code
}

int main() {
  foo<A>();
  foo<B>();
}

where I have introduced two empty "Tag" classes to indicate which bar to use and templated foo and bar based on the tag class. This seems to do the trick. Questions:

  1. Does this technique have a name? is this an example of "Tag dispatching"? From what I read about Tag dispatching it is slightly different and involves function overloading with a tag parameter. A tag that may have come from a typedef in a trait class.
  2. Is there a more idomatic compile-time technique of achieving the same thing?

Edit: Another possibility would be to use function overloading of bar instead of template specialization and pass the tag class as a parameter:

struct A { };
struct B { };

void bar(A) { }
void bar(B) { }

template<typename Tag> void foo() {
  // Duplicate code
  bar(Tag());
  // More duplicate code
}

int main() {
  foo<A>();
  foo<B>();
}
user3502661
  • 123
  • 1
  • 4

1 Answers1

7

This isn't tag dispatching. As you rightly said in your question, that'd be if you used some compile time trait of A and B to distinguish between the two, and then use that to select between two different overloads.

An good example of tag dispatch would be how std::advance is typically implemented. The function's signature is

template< class InputIt, class Distance >
void advance( InputIt& it, Distance n );

it can be advanced n positions in a single operation if it meets the requirements of RandomAccessIterator. For lesser iterators we must advance it in a loop. So an implementation would probably do something similar to the following:

namespace detail
{
  template<class InputIt, class Distance>
  void advance(InputIt& it, Distance n, std::random_access_iterator_tag) 
  {
    it += n;
  }

  template<class InputIt, class Distance>
  void advance(InputIt& it, Distance n, std::bidirectional_iterator_tag) 
  {
    if(n < 0) {
      while(n++) --it;
    } else {
      while(n--) ++it;
    }
  }

  template<class InputIt, class Distance>
  void advance(InputIt& it, Distance n, std::input_iterator_tag) 
  {
    assert(n >= 0);
    while(n--) ++it;
  }
}

template< class InputIt, class Distance >
void advance( InputIt& it, Distance n )
{
  detail::advance(it, n, 
                  typename std::iterator_traits<InputIt>::iterator_category());
}

I don't know of any specific name for what you're doing. It's just an example of how one would follow the DRY principle.

If bar took an instance of A and B as an argument, then I'd implement this differently. Instead of making bar a function template, and then providing specializations, I'd let overload resolution do the job for me.

void bar(A const&) { ... }
void bar(B const&) { ... }

But since that's not the case, providing explicit specializations seems the right way to do this.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Try CRTP to fix OP's problem. Dispatch method in base calls free function, CRTP impl invokes `self()->foo(...)`, which either calls dispatch to free or (if child overrides) class local? – Yakk - Adam Nevraumont Jun 01 '14 at 10:35
  • @Praetorian In the `std:advance` example, it looks like a tag class is constructed and copied every time, a class that is not used. Can you rely on the compiler to optimize this away? – user3502661 Jun 01 '14 at 10:57
  • @Praetorian. I could pass an instance of the empty `A` or `B` class to `bar` and not use it. I've added an edit to my question to show how. But is this any better? – user3502661 Jun 01 '14 at 13:23
  • @user3502661 About the tag struct being optimized away - can it be guaranteed? No, but it is an empty struct and the parameter is unnamed, so it's extremely likely it will be. You can certainly pass an empty instance of `A` and `B` for tag dispatch, but IMO that feels a little artificial. Whether it's a good idea would also depend on how trivial those two types are to construct. Finally, as Yakk mentioned in the comments, you can use CRTP as the solution to your problem. [Here's](http://stackoverflow.com/a/262984/241631) a good example. Wikipedia has an entry with a good example too. – Praetorian Jun 01 '14 at 17:46