Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this "Tag Dispatching"?

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>();
}
like image 855
user3502661 Avatar asked Jun 01 '14 04:06

user3502661


1 Answers

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.

like image 185
Praetorian Avatar answered Jan 18 '23 07:01

Praetorian