Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 423
Xaqq Avatar asked Oct 19 '22 22:10

Xaqq


1 Answers

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.

like image 83
Piotr Skotnicki Avatar answered Oct 28 '22 21:10

Piotr Skotnicki