0xe3aad

0xe3aad

可变参数模板的应用

引入#

C++中经常打印变量来调试代码,但无论是printf还是cout总是很麻烦:

  • 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';
    

可变参数宏#

可变参数宏是C99引入的一个特性,C++11开始支持。

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

可变参数模板#

C++11允许模板定义有任意类型任意数量的模板参数(包括 $0$ 个模板参数)。

template<typename... Values> class tuple;

如果不希望有 $0$ 个模板实参,可以如下声明:

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

可变参数模板适用于函数模板,例如printf函数的实现:

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

参数包的展开#

  • 对于模板函数
    • 递归展开
    // 递归终止函数
    void print() {}
    
    template<typename T, typename... Args>
    void print(T head, Args... args) {
      std::cout << head << (sizeof...(args) > 0 ? ", " : "");
      print(args...);
    }
    
    • 逗号表达式展开:这种方式有点繁琐,不太好理解,不推荐
  • 对于模板类
    • 递归展开
    • 继承展开

宏运算符#

  • #:将宏参数转换为字符串

    #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" );
    }
    

    使用gcc -E预处理之后,得到如下结果

    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");
    }
    
  • #@:将参数用单引号括起来,作为一个字符处理

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

    预处理之后得到:

    c = 'b';
    
  • ##:将两个独立的token连接在一起

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

    预处理之后得到:

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

实现DBG#

掌握了以上技能,我们就可以实现一个简单的调试宏:

#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;
}

上面的代码输出:

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

References#

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。