Xaqq
Xaqq

Reputation: 4386

Variadic template extractions

I faced something I cannot understand nor find a way to work around properly. What I am trying to achieve looks relatively simple: I want to compare some data.

Best way to describe would be a line of code:

std::tuple<const char *, int, const char *, int> my_data =
    std::make_tuple("hello", 13, "world", 37);
// Now I want to compare my_data againt some known value.
assert(Test::entry(my_data, "hello", 13, "world", 37));

I am using a tuple for the sake of the example. I my case, those data comes from a message object and are extracted using operator>>. However, this is not related to the problem.

Here is a minimalist code to illustrate the problem.

#include <cstring>
using MyTuple = std::tuple<const char *, int, const char *, int>;

namespace Test
{

  // called when we are done                                                                                                                                                                 
  bool extract(MyTuple source)
  {
    return true;
  }

  template<typename T,
           typename ...Content>
  bool extract(MyTuple source, T data, Content... content)
  {
    if (std::is_same<const char *, T>::value)
      assert(0); // why ? :(                                                                                                                                                                 
    std::cout << "Generic: " << data << std::endl;
    return extract(source, content...);
  }

  template<typename ...Content>
  bool extract(MyTuple source, const char *str, Content... content)
  {
    std::cout << "Overloaded: " << str << std::endl;
    return extract(source, content...);
  }

  template<typename ...Content>
  bool entry(const std::tuple<const char *, int, const char *, int> &data,
             Content... content)
  {
    return extract(data, content...);
  }

};

I would normaly perform comparaison in the extract() functions, but for keeping the example simple, I removed them.

What I want to achieve is proper dispatching. Based on the example it is my understanding that call order should be:

  1. Overloaded for const char *
  2. Generic
  3. Overload for const char *
  4. Generic

However, the output of this test program is:

  1. Overloaded: hello
  2. Generic: 13
  3. Assertion failed. The std::is_same test trigger the assert.

What I discovered was that the const char * overload will not be called after the generic overload has been call once.

I am missing something?

EDIT: Also not that if I define the const char * overload before the generic function, this wont even compile.

Upvotes: 5

Views: 171

Answers (1)

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48447

Also note that if I define the const char * overload before the generic function, this wont even compile.

A declaration should suffice:

template <typename... Content>
bool extract(MyTuple source, const char *str, Content... content); // <- here

template <typename T, typename... Content>
bool extract(MyTuple source, T data, Content... content)
{
    std::cout << "Generic: " << data << std::endl;
    return extract(source, content...);
}

template <typename... Content>
bool extract(MyTuple source, const char *str, Content... content)
{
    std::cout << "Overloaded: " << str << std::endl;
    return extract(source, content...);
}

Why?

The overload taking const char* is not visible during this particular name lookup without prior declaration. Some of the exceptions to this can be found in comments below.

Upvotes: 3

Related Questions