【问题标题】:Macro metaprogramming horror宏元编程恐怖
【发布时间】:2013-04-13 22:48:29
【问题描述】:

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

custommacro x;

这将扩展为:

declareSomething; int x; declareOtherthing;

这可能吗?

我已经用operator= 欺骗过它一次,但它不能用声明来完成。

【问题讨论】:

  • 你要声明什么?可能还有其他方法。 custommacro(x); 也是显而易见的方式。
  • 重点是不要在x 周围加上括号。
  • 不可能,在我看来,这样做非常棘手和微妙。您应该使用 () 和所有大写字母来明确实际发生的情况。试图隐藏事物或制造“魔法”几乎总是使事情复杂化。就做CUSTOMMACRO(x);
  • “我已经用 operator= 欺骗过它一次”——我很好奇这是怎么回事。
  • 我希望这是一场模糊的 C++ 编码竞赛,而不是严肃的编程工作:)

标签: c++ macros c-preprocessor metaprogramming


【解决方案1】:

只要你愿意接受两个加法,括号可以省略:

  1. 整个代码需要封装在一个块宏中
  2. 需要在 echo 指令之后添加一些内容

例如因此:

#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. 因此,我们需要插入括号;反过来,我们需要在指令之后有 something,例如 done 宏,它将扩展为包含必要关闭括号的表单
  3. 不幸的是,因为echo ... done 表单看起来不像是对预处理器的宏调用,所以当预处理器进入它时,它没有被标记为扩展,我们是否放入括号是无关紧要的。因此,仅使用 echo ... done 将在文本中转储 ECHO_MACRO 调用
  4. 文本被重新扫描,标记为展开,当它是类函数宏的参数时再次展开,所以用块宏(这里是DSL)包装整个块将导致对 ECHO_MACRO 的调用在此重新扫描过程中进行扩展(DSL 对结果没有任何作用:它的存在只是为了强制重新扫描)
  5. 我们需要将echo扩展中的(隐藏在简单宏LPAREN后面,否则宏体中不匹配的括号会混淆预处理器

如果您想为此类命令创建完整的域特定语言,您还可以通过使核心命令更加笨拙来减少 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 行,以便有一个打开命令来关闭第一个实际操作,否则括号将不匹配。

在 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!";
}

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

【讨论】:

    【解决方案3】:

    您可以使用 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 循环范围。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-10
      • 1970-01-01
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多