0xe3aad

0xe3aad

Application of Variadic Templates

Introduction#

In C++, it is common to print variables for debugging code, but both printf and cout can be cumbersome:

  • printf

    int a = 1;
    float b = 2.0;
    char c = 'c';
    printf("a = %d, b = %f, c = %c", a, b, c);
    
  • cout

    int a = 1;
    float b = 2.0;
    char c = 'c';
    std::cout << "a = " << a << ", b = " << b << ", c = " << c << '\n';
    

Variadic Macros#

Variadic macros are a feature introduced in C99 and supported since C++11.

#define def_name(...) def_body(__VA_ARGS__)

Variadic Templates#

C++11 allows template definitions with any number of template parameters of any type (including $0$ template parameters).

template<typename... Values> class tuple;

If you do not want to allow $0$ template arguments, you can declare it as follows:

template<typename First, typename... Rest> class tuple;

Variadic templates are applicable to function templates, such as the implementation of the printf function:

template<typename... Args>
void printf(const std::string &str_format, Args... args);

Expansion of Parameter Packs#

  • For template functions
    • Recursive expansion
    // Recursive termination function
    void print() {}
    
    template<typename T, typename... Args>
    void print(T head, Args... args) {
      std::cout << head << (sizeof...(args) > 0 ? ", " : "");
      print(args...);
    }
    
    • Comma expression expansion: This approach is a bit cumbersome and not easy to understand, not recommended
  • For template classes
    • Recursive expansion
    • Inheritance expansion

Macro Operators#

  • #: Converts a macro parameter to a string

    #include <stdio.h>
    #define stringer( x ) printf( #x "\n" )
    int main() {
      stringer( In quotes in the printf function call );
      stringer( "In quotes when printed to the screen" );
      stringer( "This: \"  prints an escaped double quote" );
    }
    

    After preprocessing with gcc -E, the following result is obtained:

    int main() {
      printf("In quotes in the printf function call" "\n");
      printf("\"In quotes when printed to the screen\"" "\n");
      printf("\"This: \\\"  prints an escaped double quote\"" "\n");
    }
    
  • #@: Encloses the argument in single quotes, treating it as a character

    #define makechar(x)  #@x
    c = makechar(b);
    

    After preprocessing:

    c = 'b';
    
  • ##: Concatenates two independent tokens together

    #define paster(n) printf("token" #n " = %d", token##n)
    int token9 = 9;
    paster(9);
    

    After preprocessing:

    printf("token9 = %d", token9);
    

Implementing the DBG Macro#

With the above skills, we can implement a simple debugging macro:

#include <iostream>
#include <string>

template <typename T>
void _DBG(const char *s, T t) {
  std::cout << s << "=" << t << '\n';
}

template <typename T, typename... Args>
void _DBG(const char *s, T t, Args... args) {
  while (*s != ',')
    std::cout << *s++;
  std::cout << "=" << t << ",";
  _DBG(s + 1, args...);
}

#define DBG(...) _DBG(#__VA_ARGS__, __VA_ARGS__)

int main() {
  int a = 1;
  float b = 2.0;
  char c = 'c';
  std::string d = "def";
  DBG(a, b, c, d);
  return 0;
}

The above code outputs:

a=1, b=2, c=c, d=def 

References#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.