zoukankan      html  css  js  c++  java
  • [转]用宏展开代码

    http://www.cnblogs.com/li_shugan/archive/2010/12/18/1903884.html

    “宏”这个玩意儿可能会触动很多人抵触的情绪,我也一样:很讨厌它。通常我不会用它进行计算,只有在合适的时候(比如能让我少打一些字,或者能增强代码的可读)才会请出它来。好了,言归正转,现在我要将一个代码片段有规律地重复N次,更具体点,就是在定义一个模板的时候,参数列表会很长,但是这些参数的名字是很有规律地:依次为typename T1,typename T2,....typename TN:

    1
    2
    3
    template<typename T1,typename T2,...,typename TN>
    class ManyTemplateParms
    {};

    在C++还没有支持模板的List参数(《C++ template》第13章)之前,我们可以利用宏来减少我们的工作量。

      想一下吧,如果问题变了,现在的问题是要你写一个函数打印出上面的模板参数列表,你会怎么干?拿到这个需求时,我写下了两个函数:

    • 1
      printMetaExpr: 接收一个参数i,打印出 typename Ti,
    •    print:接收一个参数N,作为需要打印的参数个数,然后在一个for循环中调用printMetaExprN次,如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <iostream>
    void printMetaExpr(int i)
    {
        std::cout<<"typename T"<<i<<",";
    }
     
    void print(int n)
    {
        for (int i = 0;i < n;++i)
        {
            printMetaExpr(i);
        }

         宏其实也和stream差不多,只不过宏不是把这些东西输出到控制台或者文件中,而是输出给编译器看的,本质上没什么区别,所以我们可以这样来设计我们的宏:

    •   定义宏DEFINE_TEMPLATE_PARAM(N),来展开typename TN,这个比较简单:#define DEFINE_TEMPLATE_PARAM(N) typename T##N
    •   定义一个可以调用DEFINE_TEMPLATE_PARAM的宏CALL_MACRO(N,callee,sep):这个宏会调用callee N次,并且每一次调用后都会加上一个分隔sep.

         难点就在每二步了,如何来循环调用宏呢?搔头了吧,其实也不是十分地难,还是拿上面打印到控制台的程序来说吧,如果你将print写了成递归的形式,一切就真相大白啦:

    复制代码
    void print(int n)
    {
    if (1== n)
    {
    printMetaExpr(
    1);
    }
    else
    {
    print(n
    -1);
    printMetaExpr(n);
    }
    }
    复制代码

         按照这个思路,如果说我们要支持最多可以调用5次,我们就可以写出下面的递归宏啦:

    1
    2
    3
    4
    5
    6
    #define CALL_MACRO0(callee)     
    #define CALL_MACRO1(callee)   calee(1)  
    #define CALL_MACRO2(callee)   CALL_MACRO1(callee) , callee(2)
    #define CALL_MACRO3(callee)   CALL_MACRO2(callee) , callee(3)  
    #define CALL_MACRO4(callee)   CALL_MACRO3(callee) , callee(4) 
    #define CALL_MACRO5(callee)   CALL_MACRO4(callee) , callee(5) 

         最后用下面的宏封装一下上面的宏

        #define CALL_MACRO(N,callee) CALL_MACRO##N(callee)

         为了验证上述宏的正确性,我向大家隆重介绍一个可以调试宏的宏。

    #define PRINT_MACRO_( x,...) #x #__VA_ARGS__
    #define PRINT_MACRO( x ) PRINT_MACRO_( x )

        好了,现在可以写一个简单地程序来测试一下成果啦:

    复制代码
    代码
    #define DEFINE_TEMPLATE_PARAM(N) typename T##N
    #define CALL_MACRO0(callee)
    #define CALL_MACRO1(callee) callee(1)
    #define CALL_MACRO2(callee) CALL_MACRO1(callee) , callee(2)
    #define CALL_MACRO3(callee) CALL_MACRO2(callee) , callee(3)
    #define CALL_MACRO4(callee) CALL_MACRO3(callee) , callee(4)
    #define CALL_MACRO5(callee) CALL_MACRO4(callee) , callee(5)

    #define CALL_MACRO(N,callee) CALL_MACRO##N(callee)

    #define PRINT_MACRO_( x,...) #x #__VA_ARGS__
    #define PRINT_MACRO( x ) PRINT_MACRO_( x )

    int main(int argc, char* argv[])
    {
    std::cout
    <<PRINT_MACRO(CALL_MACRO(5,DEFINE_TEMPLATE_PARAM))<<std::endl;
    return0;
    }
    复制代码

    运行后输出:

       下面给出我使用宏的一点心得:

       宏的性质:

    •    #可以将传入的参数变为字符串,PRINT_MACRO使用了这个思想,##用以连接宏参数和其他的字符,例如如果有宏
    1
    #defineMEMBER(name)   m_##name;

         调用 MEMBER(Num)  你会得到 m_Num;

    •     宏不能实现真正意义上的递归调用,例如:
    1
    #define Add(n)  Add(n – 1) + n

         调用Add(3),你将得到:Add(3 – 1) + 3,这恐怕不是你想要的。为了实现宏的递归调用必须像上面定义CALL_MACRO一样一步步地递归。

    •   我将宏分成了两类:

               (1)值宏,例如: #define __COMMA__ ,

               (2)函数宏,例如CALL_MACRO。函数宏又分为两种

                          I. 可调用宏,也就是可以作为其他宏的参数被调用,例如DEFINE_TEMPLATE_PARAM

                          II.不可调用宏,由于#和##的存在。例如: CALL_MACRO

    •  定义一个宏时要注意的是:

                (1). 只将宏用在展开代码的时候。其他的功能用inline函数和模板代替吧。

                (2). 如果你在写一个可调用宏,对于传入的函数宏,要让它可以展开,值宏只能在最下层展开.比如逗号,为了让它只在最底层展开,你可以这样来定义它:              

    #define _COMMA_(x) ,

         .传参时,传入_COMMA_,在最底层调用时,用_COMMA_(1)

  • 相关阅读:
    pytorch获取模型某一层参数名及参数值方式
    PyTorch源码解读之torchvision.models
    torch.nn.Conv2d()函数详解
    UCF101
    ipynb格式转换
    linux下的压缩与解压缩
    tomcat 部署的项目图片名字带中文
    恢复oracle误删除数据
    项目中遇到的分布式高并发情况及解决方案
    poi 导出复杂表头 execl
  • 原文地址:https://www.cnblogs.com/logitechlike/p/2886807.html
Copyright © 2011-2022 走看看