c++ - C++ 宏中的函数式编程风格 : Is this documented anywhere?

标签 c++ c-preprocessor x-macros

阅读一些 C++ 代码时,我遇到了我称之为函数宏的“功能性”使用,大致如下(这是一个完全程式化的例子来说明这一点):

#define TOP_LEVEL(ARG1)     \
ARG1("foo1","bar1")     \
ARG1("foo2","bar2")

#define NEXT_LEVEL(ARG2A, ARG2B)    \
cout << ARG2A << " and " << ARG2B;

TOP_LEVEL(NEXT_LEVEL)

我对这门语言比较陌生,一开始我想不通,但后来我只通过预处理器 (g++ -E) 运行了它,你看它解析为:

cout << "foo1" << " and " << "bar1"; cout << "foo2" << " and " << "bar2";

你看到它在那里做了什么吗?它将宏 NEXT_LEVEL 像函数指针一样 传递给宏 TOP_LEVEL。看到这可能有多大用处,我想了解更多:将函数传递给其他函数是 pretty sophisticated stuff并且必须至少有更多关于该技术的说法。

然而,尽管进行了大量的谷歌搜索,我还是找不到证据表明预处理器的这个特性存在,更不用说任何接近文档的东西了:here , here , herehere只是跳过这一步的宏教程的四个例子;最后一个甚至有一个名为“高级宏技巧”的部分 - 这肯定符合条件!?

(请注意,这与简单地将另一个求值函数宏作为参数调用函数宏完全不同——FOO(BAR(2)) 更为直接。)

我的问题是:

  • 此行为是否有实际名称?
  • 是否在任何地方记录了它?
  • 它是常用的,还是有众所周知的陷阱等?

最佳答案

这个想法被创造为“X-Macro”。一些定义不会包含您的特定示例(X-macros 通常涉及更多,包含一个文件),但任何相关信息。 about this 将在搜索时属于该术语。

作为chris评论中提到,Boost.Preprocessor 使用了这个想法,效果很好。流行的用途是:BOOST_PP_REPEATBOOST_PP_LIST_FOR_EACH,最强大的是:BOOST_PP_ITERATE

BOOST_PP_ITERATE 是一个“真正的”X-Macro;包含单个文件会扩展为依赖于之前定义的宏的内容。我在这个 other answer 中展示了一个更“合适”的骨架框架, 但一个例子是:

// in xyz_data.def
DEFINE_XYZ(foo, 1, "Description A")
DEFINE_XYZ(bar, 5, "Description B")
DEFINE_XYZ(baz, 7, "Description C")

然后当我只想要第 1 列时,我可以这样做:

#define DEFINE_XYZ(name, number, desc) some_func(name)
#include "xyz_data.def"

在其他地方我想为每个函数生成一些函数,我可以这样做:

#define DEFINE_XYZ(name, number, desc)                              \
    int BOOST_PP_CAT(get_number_for_, name)()                       \
    {                                                               \
       std::clog << "Getting number, which is: " desc << std::endl; \
                                                                    \
       return number;                                               \
    }

#include "xyz_data.def"

然后您可以生成一个枚举,其中名称等于数字等。

强大的是,当我想添加一个新的 xyz 时,我只需将它添加到一个位置,它就会神奇地出现在任何需要它的地方。我在一个非常大的代码库中做过类似的事情,以便将一些书签数据保存在一个中央位置,但是各种属性在不同位置的使用方式不同。

请注意,通常没有办法解决这个问题;我所拥有的是在句法上不同,所以没有其他语言特性可以为我将它泛化到那个级别,只有宏。宏不是邪恶的。

您拥有的实际上是一个 X 宏,其中 .def 文件是独立的,足以成为 #define。换句话说,#include "xyz_data.def" 只是 TOP_LEVEL

这只有一个很大的缺点,具有讽刺意味的是,这不是 X-macros 本身的使用,而是它们对 C 和 C++ 编译器的影响。问题在于预处理器允许我们在每次包含文件时更改文件的预处理结果,即使文件内容完全相同也是如此。

您可能听说过,与现代语言相比,C 和 C++ 的编译速度很慢,这就是原因之一。它没有合适的模块/打包系统,只是临时包含其他文件。我们刚刚了解到,一般来说这是无法避免的。哎呀。 (也就是说,编译器很聪明,例如,当您在一个文件周围包含保护措施时,它会注意到,并避免多次处理它。但这是视情况而定。)

也就是说,使用 X-Macros 本身不应该对实际程序的编译时间产生巨大影响。只是它们仅仅是潜在的存在就触及了真实的世界,并搅乱了编译器的头脑。

关于c++ - C++ 宏中的函数式编程风格 : Is this documented anywhere?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19672644/

相关文章:

c - 使用 X 宏的特定条目

c++ - 构造函数的继承问题

c++ - 通过使用字符串调用新构造函数来创建新对象

c++ - 将 binder1st 与自定义仿函数一起使用

c - 如何使用 gcc 的 -I 命令添加递归文件夹

连接 X 宏的多个标记

c - x-macro 条件错误 - 数字比较

c++ - 无法捕获自定义 std::runtime_error

c++ - 预处理器指令在 for 循环中不起作用

java - 是否有任何支持 C 预处理器的 Java IDE?