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#

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。