简体   繁体   English

C ++宏中的函数式编程样式:是否在任何地方都有记录?

[英]Functional-Programming Style in C++ Macros: Is this documented anywhere?

Reading some C++ code I came across what I'll call a "functional" use of function Macros roughly as follows (this is a totally stylized example to make the point): 阅读一些C ++代码后,我大致看到了所谓的函数宏的“函数式”用法(大致来说,这是一个风格化的例子):

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

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

TOP_LEVEL(NEXT_LEVEL)

I'm relatively new to the language and at first I couldn't figure this out, but then I ran it through just the preprocessor ( g++ -E ) and lo and behold it resolves to: 我是该语言的新手,起初我无法弄清楚,但是后来我仅通过预处理器( g++ -E )来运行它,瞧瞧它解析为:

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

Do you see what it did there? 你看到那里做了什么吗? It passed the Macro NEXT_LEVEL like a function pointer to the Macro TOP_LEVEL. 像指向宏TOP_LEVEL 的函数指针一样传递了宏NEXT_LEVEL。 Seeing how useful this could potentially be, I wanted to learn more about it: passing around functions to other functions is pretty sophisticated stuff and there must be at least something more to say about the technique. 看到这可能有多大用处之后,我想了解更多有关它的知识:将函数传递给其他函数是相当复杂的事情,并且至少应该对这种技术说些什么。

Yet despite a ton of Googling I can't find evidence that this feature of the preprocessor even exists, let alone anything approaching documentation: here , here , here and here are just four examples of Macro tutorials that skip right past this; 尽管进行了大量的谷歌搜索,但我找不到证据表明预处理器的这一功能甚至存在,更不用说任何即将来临的文档了: 这里这里这里这里只是四个Macro教程的例子,它们跳过了这些; the last even has a section called "Advanced Macro tricks" - surely this qualifies!? 最后一个甚至有一个名为“高级宏技巧”的部分-当然可以满足要求!

(Please note this is totally different than simply calling a function macro with another evaluated function macro as an argument- FOO(BAR(2)) is much more straightforward.) (请注意,这与简单地调用带有另一个已评估函数宏作为参数的函数宏完全不同-FOO(BAR(2))更为简单。)

My questions are: 我的问题是:

  • Is there an actual name for this behavior? 有此行为的实际名称吗?
  • Is it documented anywhere? 它记录在任何地方吗?
  • It is commonly used, or are there well known pitfalls, etc.? 它是常用的,还是有众所周知的陷阱等?

The idea is coined "X-Macro". 这个想法被称为“ X-宏”。 Some definitions won't include your particular example (X-macros generally are a bit more involved, with a file being included), but any relevant info. 一些定义将不包括您的特定示例(X-macros通常会涉及到更多点,其中包含一个文件),但是会包含任何相关信息。 about this will fall under that term when searching. 搜索时,有关此字词的信息将归入该字词。

As chris mentioned in the comments, Boost.Preprocessor uses this idea to great effect. 正如克里斯在评论中提到的那样,Boost.Preprocessor使用此想法产生了很大的效果。 Popular uses are: BOOST_PP_REPEAT , BOOST_PP_LIST_FOR_EACH , and most powerfully: BOOST_PP_ITERATE . 流行的用法是: BOOST_PP_REPEATBOOST_PP_LIST_FOR_EACH ,以及最强大的用法: BOOST_PP_ITERATE

BOOST_PP_ITERATE is a "true" X-Macro; BOOST_PP_ITERATE是“真实”的X宏; including a single file is expands to something dependent on a macro defined just prior. 包括单个文件的内容将扩展为依赖于之前定义的宏的内容。 I show a more "proper" skeleton framework in this other answer , but an example would be: 我在另一个答案中展示了一个更“合适”的框架框架,但是一个例子是:

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

Then later when I just want column 1 I can do: 然后,当我只想要第1列时,我可以执行以下操作:

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

And somewhere else where I want to generate some function for each one, I can do: 在我想为每个函数生成某些函数的其他地方,我可以执行以下操作:

#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"

You can then generate an enum where the name equals the number, etc. 然后,您可以生成名称等于数字等的枚举。

The power is that when I want to add a new xyz, I just add it in one spot and it magically shows up everywhere it needs to be. 强大之处在于,当我想添加新的xyz时,只需将其添加到一个位置,它就会神奇地显示在需要的任何位置。 I have done something like this in a very large codebase to keep some bookmarking data in one central place, but the various attributes were used differently in various locations. 我已经在一个非常大的代码库中执行了类似的操作,以将一些书签数据保留在一个中央位置,但是各种属性在不同位置使用的方式有所不同。

Note that there is often no way around this; 请注意,通常无法解决此问题。 what I have are syntactically different , so no other language feature will generalize it for me to that level, only macros. 我在语法上有所不同 ,因此没有其他语言功能可以将其推广到该级别,只有宏。 Macros are not evil. 宏不是邪恶的。

What you have is effectively an X-macro where the .def file is self-contained enough to be a #define . 您所拥有的实际上是一个X宏,其中.def文件是自包含的,足以成为#define In other words, #include "xyz_data.def" is just TOP_LEVEL . 换句话说, #include "xyz_data.def"只是TOP_LEVEL

There is only one large downside to this, and ironically it's not the use of X-macros themselves but the effect they have on C and C++ compilers. 这样做只有一个很大的缺点,具有讽刺意味的是,这并不是使用X宏本身,而是它们对C和C ++编译器的影响。 The problem is that the preprocessor has allowed us to change the preprocessed result of a file every time its included, even if the file contents are exactly the same . 问题在于,即使文件内容完全相同 ,预处理器也允许我们在每次包含文件时更改文件的预处理结果。

You may have heard that C and C++ are slow to compile compared to modern languages, this is one of the reasons why. 您可能已经听说,与现代语言相比,C和C ++的编译速度较慢,这就是原因之一。 It has no proper module/packaging system, just ad-hoc inclusion of other files. 它没有适当的模块/打包系统,只是临时包含其他文件。 And we just learned, in general this cannot be avoided . 而且我们刚刚了解到, 通常这是无法避免的 Oops. 哎呀。 (That said, compilers are smart and will note when you have include guards around a file, for example, and avoid processing it multiple times. But this is situational.) (也就是说,编译器很聪明,例如会在您在文件周围包含防护措施时予以注意,并避免多次处理它。但这是根据情况而定。)

That said, using X-Macros themselves shouldn't be a huge contributor to the compilation time of a real program. 就是说,使用X-Macros本身不应该对真正程序的编译时间做出巨大贡献。 It's just that their mere potential existence reaches out into the real word and screws with compiler's heads. 仅仅是它们的潜在存在延伸到了真正的词里,并在编译器的头脑中screws绕。

Here is a few lectures : C is purely functionnal , 这里有一些讲座: C是纯函数式的

I suggest you take a look at libpp , macrofun , 我建议您看看libppmacrofun

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM