宏观元编程恐怖

时间:2013-04-13 22:48:29

标签: c++ macros c-preprocessor metaprogramming

我正在尝试做类似的事情:

custommacro x;

将扩展为:

declareSomething; int x; declareOtherthing;

这甚至可能吗?

我已经用operator=欺骗了一次,表现得像那样,但声明无法完成。

3 个答案:

答案 0 :(得分:6)

只要您愿意接受两个补充,您就可以忽略括号:

  1. 整个代码需要包含在块宏
  2. 需要有一些关于echo指令的内容
  3. e.g。正是如此:

    #define LPAREN (
    #define echo ECHO_MACRO LPAREN
    #define done )
    
    #define ECHO_MACRO(X) std::cout << (X) << "\n"
    
    #define DSL(X) X
    
    ...
    DSL(
        echo "Look ma, no brains!" done;
    )
    ...
    

    原因:

    1. 没有办法使一个类似函数的宏展开而没有括号。这只是宏语言的基本要求;如果你想要别的东西调查一个不同的宏处理器
    2. 因此,我们需要插入括号;反过来,我们需要在指令之后有某些,就像done宏一样,它将扩展为包含必要的关闭paren的表单
    3. 不幸的是,因为echo ... done形式看起来不像是对预处理器的宏调用,所以当预处理器进入时,它没有被标记为扩展,并且我们是否将parens放入是不相关的。因此,仅使用echo ... done将在文本
    4. 中转储ECHO_MACRO调用
    5. 文本被重新扫描,标记为扩展,并在它是类函数宏的参数时再次展开,因此用块宏(这里是DSL)包裹整个块将导致调用要在此重新扫描过程中扩展ECHO_MACRO(DSL对结果不做任何操作:它只是为了强制重新扫描而存在)
    6. 我们需要在简单宏(后面的echo扩展中隐藏LPAREN,否则宏体中不匹配的括号会混淆预处理器
    7. 如果您想为这些命令创建一个完整的特定于域的语言,您还可以通过使核心命令更加笨拙来减少done命令的数量:

      #define LPAREN (
      
      #define begin NO_OP LPAREN 0
      #define done );
      
      #define echo ); ECHO_MACRO LPAREN
      #define write ); WRITE_MACRO LPAREN
      #define add ); ADD_MACRO LPAREN
      #define sub ); SUB_MACRO LPAREN
      
      #define NO_OP(X) 
      #define ECHO_MACRO(X) std::cout << (X) << "\n"
      #define WRITE_MACRO(X) std::cout << (X)
      #define ADD_MACRO(D, L, R) (D) = (L) + (R)
      #define SUB_MACRO(D, L, R) (D) = (L) - (R)
      
      #define DSL(X) DSL_2 X
      #define DSL_2(X) X
      
      int main(void) {
      int a, b;
      DSL((
          begin
            add a, 42, 47
            sub b, 64, 50
            write "a is:  "
            echo a
            write "b is:  "
            echo b
          done
      ))
      return 0;
      }
      

      在这种形式中,每个命令都预先设计为关闭前面的命令,因此只有最后一个命令需要done;你需要一个begin行,这样就可以打开一个关闭第一个实际操作的命令,否则parens会不匹配。

      C语言比在C ++中容易得多,因为C的预处理器功能更强大(它支持__VA_ARGS__,这对于复杂的宏元编程非常重要)。

      哦,是的,还有一件事 -

      ...请永远在实际代码中执行此操作。

答案 1 :(得分:2)

我明白你要做什么,而且根本无法完成。宏只是文本替换,它不知道后面会发生什么,所以尝试custommacro x会扩展到custommacro是什么,一个空格,然后是x,它只是不会在语义上工作。

另外,关于你的echo hack:在C ++中使用运算符实际上非常简单:

#include <iostream>

#define echo std::cout <<

int main()
{
    echo "Hello World!";
}

但是你真的不应该编写这样的代码(也就是说,使用宏和伪问题回应)。您应该编写符合语言语法的代码以及您尝试执行的语义。如果要使用std::cout写入标准输出。此外,如果您想使用echo,请创建一个名为echo的函数,在内部调用std::cout,但不要破解该语言的功能来创建自己的函数。

答案 2 :(得分:1)

您可以使用for循环和GnuC语句表达式扩展。

#define MY_MACRO\
  FOR_MACRO(_uniq##__COUNTER__##name,{/*declareSomething*/ },{ /* declareOtherthing */ }) int

#define FOR_MACRO(NAME,FST_BLOCK,SND_BLOCK)\
  for(int NAME = ({FST_BLOCK ;0;}); NAME<1 ; NAME++,(SND_BLOCK))

它实际上是卫生的,但这意味着无论你在这些代码块中做什么都不会逃避for循环范围。