我可以附加到预处理器宏吗?

时间:2022-11-25 10:57:57

Is there any way in standard C—or with GNU extensions—to append stuff to a macro definition? E.g., given a macro defined as
#define List foo bar
can I append bas so that it List expands as if I’d defined it
#define List foo bar bas?

在标准C或GNU扩展中有什么方法可以将内容附加到宏定义中吗?例如,给定一个定义为#define List foo bar的宏,我可以追加bas,以便它扩展,就像我定义它一样#define List foo bar bas?

I was hoping I could do something like this:

我希望我能做到这样的事情:

#define List    foo bar bas

#define List_   Expand(List)
#undef List
#define List    Expand(List_) quux

but I can’t figure out how to define the Expand() macro so it’ll do what I want.

但我无法弄清楚如何定义Expand()宏,所以它会做我想要的。

Motivation: I’m playing with discriminated/tagged unions along these lines:

动机:我正在沿着以下方面玩歧视/标记的联盟:

struct quux_foo { int x; };
struct quux_bar { char *s; };
struct quux_bas { void *p; };

enum quux_type {quux_foo, quux_bar, quux_bas};

struct quux {
    enum quux_type type;
    union {
        struct quux_foo foo;
        struct quux_bar bar;
        struct quux_bas bas;
    } t;
};

I figure this is a good place for the X-macro. If I define a macro
#define quux_table X(foo) X(bar) X(bas)
the enumeration & structure can be defined thus, and never get out of sync:

我认为这是X-macro的好地方。如果我定义一个宏#define quux_table X(foo)X(bar)X(bas),则可以定义枚举和结构,并且永远不会失去同步:

#define X(t) quux_ ## t,
enum quux_type {quux_table};
#undef X

#define X(t) struct quux_ ## t t;
struct quux {
    enum quux_type type;
    union {quux_table} t;
};
#undef X

Of course, the quux_* structures can get out of sync, so I’d like to do something like this, only legally:

当然,quux_ *结构可能不同步,所以我想做这样的事情,只是合法的:

struct quux_foo { int x; };
#define quux_table quux_table X(foo)

struct quux_bar { char *s; };
#define quux_table quux_table X(bar)

struct quux_bas { void *p; };
#define quux_table quux_table X(bas)

(Well, what I really want to be able to do is something like
member_struct(quux, foo) { int x; };
but I’m well aware that macros cannot be (re)defined from within macros.)

(好吧,我真正希望能做的是像member_struct(quux,foo){int x;};但是我很清楚宏不能从宏中重新定义。)

Anyhow, that’s my motivating example. Is there a way to accomplish this?

无论如何,那是我激励的榜样。有办法实现这个目标吗?

Boost.Preprocessor examples are fine, if you can show me how to make the X-macro technique work with that library.

Boost.Preprocessor示例很好,如果您可以告诉我如何使X-macro技术与该库一起工作。

3 个解决方案

#1


6  

Effectively, no.

Macros are lazily evaluated. When you #define List_ Expand(List), its replacement list is the sequence of four tokens Expand, (, List, and ). There isn't any way to expand a macro into a replacement list.

宏被懒惰地评估了。当#define List_ Expand(List)时,其替换列表是四个标记Expand,(,List和)的序列。没有任何方法可以将宏扩展为替换列表。

All macro replacement takes place when a macro is invoked.

所有宏替换都在调用宏时发生。

I'd recommend looking at using the Boost.Preprocessor library for automatic code generation. It's a bit of work, but you can accomplish some fairly impressive things using it. It should be fully compatible with C.

我建议使用Boost.Preprocessor库来自动生成代码。这有点工作,但你可以使用它完成一些相当令人印象深刻的事情。它应该与C完全兼容。

#2


3  

There is a way!

有一种方法!

Using the new _Pragma keyword this can be achieved in gcc (though not with msvc)

使用新的_Pragma关键字,这可以在gcc中实现(尽管不是用msvc)

If you pop a macro within it's own definition it will delay it's expansion until the macro is expanded for the first time. This allows you to make it's previous expansion part of it's own definition. However, since it is popped during it's expansion, it can only be used once

如果你在它自己的定义中弹出一个宏,它将延迟它的扩展,直到宏第一次扩展。这允许你使它成为它自己定义的先前扩展部分。但是,由于它在扩展期间弹出,因此只能使用一次

Here is some sample code to see it in action

以下是一些示例代码,可以看到它的实际效果

#define pushfoo _Pragma("push_macro(\"foo\")") //for convenience
#define popfoo _Pragma("pop_macro(\"foo\")")

#define foo 1

pushfoo                           //push the old value
#undef foo                        //so you don't get a warning on the next line
#define foo popfoo foo , 2        //append to the previous value of foo

pushfoo
#undef foo
#define foo popfoo foo , 3

pushfoo
#undef foo
#define foo popfoo foo , 4


foo //this whole list will expand to something like popfoo foo popfoo foo popfoo foo , 4
    //which will in turn expand to 1 , 2 , 3 , 4

foo //the second time this will expand to just 1

This option should make automatic code generation a fair bit easier, though unfortunately only on gcc (maybe clang, haven't tested)

这个选项应该使自动代码生成更容易,但不幸的是只在gcc上(可能是clang,还没有测试过)

To be honest there is no reason I can find why this must work, it is most probably undefined behavior that happens to work. I'm guessing the reason is that after popping foo, the current macro being expanded is no longer associated with the name foo which allows the symbol foo to be expanded, but that is only my conjecture

说实话,没有理由我可以找到为什么这必须起作用,这很可能是未定义的行为恰好起作用。我猜测的原因是,在弹出foo之后,当前正在扩展的宏不再与名称foo相关联,这允许扩展符号foo,但这只是我猜想的

Edit:

After testing on clang, this does not does work on clang.

在对clang进行测试之后,这对clang不起作用。

I don't know why I thought clang did not work, maybe it didn't on a different machine. I definitely did get it to work with the code given though

我不知道为什么我认为clang不起作用,也许它不是在不同的机器上。我确实让它使用了给出的代码

#3


2  

I'm not sure if this helps, but you can do vari arg macros. Mr. Conrad of the x264 project loves preprocessor abuse. If they sound like they might help you can find out more Here

我不确定这是否有帮助,但你可以做vari arg宏。 x264项目的康拉德先生喜欢预处理器滥用。如果他们听起来像他们可能会帮助你可以在这里找到更多

#1


6  

Effectively, no.

Macros are lazily evaluated. When you #define List_ Expand(List), its replacement list is the sequence of four tokens Expand, (, List, and ). There isn't any way to expand a macro into a replacement list.

宏被懒惰地评估了。当#define List_ Expand(List)时,其替换列表是四个标记Expand,(,List和)的序列。没有任何方法可以将宏扩展为替换列表。

All macro replacement takes place when a macro is invoked.

所有宏替换都在调用宏时发生。

I'd recommend looking at using the Boost.Preprocessor library for automatic code generation. It's a bit of work, but you can accomplish some fairly impressive things using it. It should be fully compatible with C.

我建议使用Boost.Preprocessor库来自动生成代码。这有点工作,但你可以使用它完成一些相当令人印象深刻的事情。它应该与C完全兼容。

#2


3  

There is a way!

有一种方法!

Using the new _Pragma keyword this can be achieved in gcc (though not with msvc)

使用新的_Pragma关键字,这可以在gcc中实现(尽管不是用msvc)

If you pop a macro within it's own definition it will delay it's expansion until the macro is expanded for the first time. This allows you to make it's previous expansion part of it's own definition. However, since it is popped during it's expansion, it can only be used once

如果你在它自己的定义中弹出一个宏,它将延迟它的扩展,直到宏第一次扩展。这允许你使它成为它自己定义的先前扩展部分。但是,由于它在扩展期间弹出,因此只能使用一次

Here is some sample code to see it in action

以下是一些示例代码,可以看到它的实际效果

#define pushfoo _Pragma("push_macro(\"foo\")") //for convenience
#define popfoo _Pragma("pop_macro(\"foo\")")

#define foo 1

pushfoo                           //push the old value
#undef foo                        //so you don't get a warning on the next line
#define foo popfoo foo , 2        //append to the previous value of foo

pushfoo
#undef foo
#define foo popfoo foo , 3

pushfoo
#undef foo
#define foo popfoo foo , 4


foo //this whole list will expand to something like popfoo foo popfoo foo popfoo foo , 4
    //which will in turn expand to 1 , 2 , 3 , 4

foo //the second time this will expand to just 1

This option should make automatic code generation a fair bit easier, though unfortunately only on gcc (maybe clang, haven't tested)

这个选项应该使自动代码生成更容易,但不幸的是只在gcc上(可能是clang,还没有测试过)

To be honest there is no reason I can find why this must work, it is most probably undefined behavior that happens to work. I'm guessing the reason is that after popping foo, the current macro being expanded is no longer associated with the name foo which allows the symbol foo to be expanded, but that is only my conjecture

说实话,没有理由我可以找到为什么这必须起作用,这很可能是未定义的行为恰好起作用。我猜测的原因是,在弹出foo之后,当前正在扩展的宏不再与名称foo相关联,这允许扩展符号foo,但这只是我猜想的

Edit:

After testing on clang, this does not does work on clang.

在对clang进行测试之后,这对clang不起作用。

I don't know why I thought clang did not work, maybe it didn't on a different machine. I definitely did get it to work with the code given though

我不知道为什么我认为clang不起作用,也许它不是在不同的机器上。我确实让它使用了给出的代码

#3


2  

I'm not sure if this helps, but you can do vari arg macros. Mr. Conrad of the x264 project loves preprocessor abuse. If they sound like they might help you can find out more Here

我不确定这是否有帮助,但你可以做vari arg宏。 x264项目的康拉德先生喜欢预处理器滥用。如果他们听起来像他们可能会帮助你可以在这里找到更多