有没有办法用 C 宏代替普通的 C 代码

发布于 2024-11-15 06:22:45 字数 137 浏览 0 评论 0原文

我想在 C 文件上运行工具 x 并获取后宏代码。 (如果我们能只做宏那样的功能就更好了)。我知道 gcc -E ,但这也将所有内容包含到一个大文件中。

基本上,我想使用一些 C 宏来执行重复代码,但不希望最终代码包含任何宏,因为项目不赞成它们。

I want to run tool x on a C file and get the post-macro code. (If we can do only function like macros even better). I know about gcc -E but that does all the includes into one big file as well.

Basically I'd like to use some C macros for repetitive code but don't want the final code to contain any macros because they are frowned upon by the project.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凑诗 2024-11-22 06:22:45

使用您选择的脚本语言,注释掉所有#include,然后运行gcc -E -Wp,-P,-C,-CC foo.c,然后取消注释#include 。或者您可以将 #include 替换为一些不以 # 开头的字符串...例如,include#@包括;可能性是无限的。使用 @ 而不是 # 的方法使您可以完全控制哪些预处理器指令会被扩展,哪些不会被扩展...编写您不想扩展的指令@,然后脚本只运行gcc -E,然后将@更改为#。但是,我认为最好以相反的方式进行,使用特殊标记(例如,@)来指示您的可扩展宏。然后脚本会将前导# 转换为其他内容(例如HIDE#)并将标记(例如@)转换为< code>#,运行 gcc -E,然后将 HIDE# (或其他)转回 #

-Wp 指定预处理器选项。 -P 表示不生成行指令,-C 表示不删除注释,-CC 表示不删除生成的注释通过宏——这意味着代码生成宏中的注释将保留在输出中。要确定所有可用的预处理器选项(有很多,大部分不感兴趣),请运行 gcc -Wp,--help anyfile.c ......这就是我得出这个答案的方法(首次运行 gcc --help 找到 -Wp 选项后)。 (知道如何发现事物比了解事物更重要。)

Using the scripting language of your choice, comment out all #includes, then run gcc -E -Wp,-P,-C,-CC foo.c then uncomment the #includes. Or you could replace #include with some string that doesn't start with a # ... e.g., include# or @include; the possibilities are endless. The approach of using @ instead of # gives you complete control over which preprocessor directives do and don't get expanded ... code the ones you don't want expanded with @, and then the script just runs gcc -E and then changes @ to #. However, I think it would be better to do it the other way around, using a special marker (e.g., @) to indicate your expandable macros. Then the script would turn leading #s into something else (e.g., HIDE#) and turn the marker (@, for instance) into #, run gcc -E, then turn HIDE# (or whatever) back into #.

-Wp specifies preprocessor options. -P means don't generate line directives, -C means don't delete comments, and -CC means don't delete comments generated by macros -- that means that comments in your code-generating macros will be preserved in the output. To determine all available preprocessor options (there are a great many, mostly not of interest), run gcc -Wp,--help anyfile.c ... that's what I did to come up with this answer (after first running gcc --help to find the -Wp option). (Knowing how to find things out is more important than knowing things.)

野の 2024-11-22 06:22:45

如何在代码中的 #include 列表之后放置一个分隔符,以便您可以手动删除包含文件扩展,但在运行 gcc -E< 后保持宏扩展完好无损/代码>?

比如:

#include <one>
#include <two>
void delete_everything_above_and_put_includes_back(); // delimeter
#define MACRO(X) ...
//rest of the code

我不知道有什么工具可以扩展宏但不会扩展#include...

How about putting a delimiter in your code right after the #include list, so that you could get rid of the include files expansions manually, but have the macro expansion intact after running gcc -E?

Something like:

#include <one>
#include <two>
void delete_everything_above_and_put_includes_back(); // delimeter
#define MACRO(X) ...
//rest of the code

I'm not aware of a tool that expands macros but doesn't expand #includes...

心作怪 2024-11-22 06:22:45

我决定添加另一个答案,因为它完全不同。

您是否考虑过使用 const 变量和内联函数作为替代方案,而不是使用技巧将宏扩展到项目源存储库中?

基本上,这些就是宏在您的项目中“不受欢迎”的原因。

您必须记住,inline 只是一个“建议”(即:该函数实际上可能不是内联的),并且 const 将使用内存而不是常量文字(嗯,取决于编译器,好的编译器会优化),但这会做两件事:

  1. 让你的代码遵守项目编码标准(这总是一件好事,至少在政治上,如果不一定在技术上)
  2. 不会要求代表您保留的其他隐藏脚本或操作代码可重用且可维护(我假设您想使用宏以避免重复代码,对吧?)

因此,请记住这一点,作为一种选择。

I decided to add another answer, because it's entirely different.

Instead of having tricks to expand macros into the project source repository - have you considered using const variables and inline functions as alternative?

Basically those are the reasons that the macros are "frown upon" in your project.

You have to keep in mind that inline is merely a "suggestion" (i.e.: the function might not be in fact inlined) and const will use memory instead of being a constant literal (well, depends on compiler, good compiler will optimize), but that will do two things:

  1. Keep your code in adherence to the project coding standards (which is always a good thing, at least politically if not necessarily technically)
  2. Will not require additional hidden scripts or actions on your behalf to keep the code reusable and maintainable (I'm assuming you want to use macros in order to avoid repetitive code, right?)

So keep that in mind as well, as an option.

情绪失控 2024-11-22 06:22:45

作为问题的可能解决方案:“编写一个宏,然后通过用等效函数替换来丢弃它”,您可以使用原型函数式宏。它们有一些局限性,使用时必须小心。但它们的工作方式几乎与函数相同。

#define xxPseudoPrototype(RETTYPE, MACRODATA, ARGS) typedef struct { RETTYPE xxmacro__ret__; ARGS } MACRODATA

xxPseudoPrototype(float, xxSUM_data, int x; float y; );
xxSUM_data xxsum;
#define SUM_intfloat(X, Y) ( xxsum = (xxSUM_data){ .x = (X), .y = (Y) }, \
    xxsum.xxmacro__ret__ = xxsum.x + xxsum.y, \
    xxsum.xxmacro__ret__)

我已经在这里解释了详细信息(主要是第 4 节,对于类似函数的宏):

宏伪造函数

  • 第一行定义了一个宏,可用于为宏声明伪原型
  • 第二行使用提供这样一个伪原型的宏。它定义了所需类型的“形式”参数。它按顺序包含宏所需的“返回”类型、保存宏参数的结构体名称,最后是宏的参数(带有类型!)。我更喜欢称它们为伪参数
  • 第三行是强制性声明,它使“真实”成为伪参数。它声明了一个需要编写的结构。它定义了一个包含“真实”版本的伪参数的结构。
  • 最后,宏本身被定义为表达式的链表,用逗号运算符分隔。第一个操作数用于将宏的参数“加载”到“真实”类型参数中。最后一个操作数是“返回值”,它也具有所需的类型。

观察编译器对类型进行正确且透明的诊断。
(但是,有必要谨慎对待这些结构,如链接中所述)。

现在,如果您可以将宏的所有句子收集为以逗号分隔的函数调用链,那么您就可以根据需要获得类似函数的宏。

此外,您可以轻松地将其转换为实际函数,因为参数列表已经定义。类型检查已经完成,所以一切都会正常进行。
在上面的示例中,您必须将所有行(第一行除外)替换为其他行:

#define xxPseudoPrototype(RETTYPE, MACRODATA, ARGS) typedef struct { RETTYPE xxmacro__ret__; ARGS } MACRODATA

float SUM_intfloat(int x, float y) {                       /* (1) */
   xxPseudoPrototype(float, xxSUM_data, int x; float y; ); /* (2) */
   xxSUM_data xxsum;                                       /* (2) */
   return                                                  /* (3) */
     ( xxsum = (xxSUM_data){ .x = x, .y = y },             /* (4) (5) (6) */
         xxsum.xxmacro__ret__ = xxsum.x + xxsum.y,         /* (5) (6) */
         xxsum.xxmacro__ret__)                             /* (6) */ 
    ;                                                      /* (7) */
 }                                                         /* (8) */

替换将遵循系统过程:

(1) 宏头变成函数头。分号 (;) 替换为逗号 (,)。
(2) 声明行移至函数体内。
(3)加了“回”字。
(4) 宏参数X、Y被函数参数x、y替代。
(5) 删除所有结尾的“\”。
(6) 所有中间计算和函数调用均保持不变。
(7) 添加分号。
(8) 关闭函数体。

问题:虽然此方法可以满足您的需求,但请注意该函数已重复其参数列表。这不好:必须删除伪原型和重复项:

float SUM_intfloat(int x, float y) { 
   return  
     ( x + y )
    ;  
 }      

As a possible solution to your problem: "write a macro and then discard it, by replacing by an equivalent function", you can use prototyped function-like macros. They have some limitations, and must be used with some care. But they work almost the same way as a function.

#define xxPseudoPrototype(RETTYPE, MACRODATA, ARGS) typedef struct { RETTYPE xxmacro__ret__; ARGS } MACRODATA

xxPseudoPrototype(float, xxSUM_data, int x; float y; );
xxSUM_data xxsum;
#define SUM_intfloat(X, Y) ( xxsum = (xxSUM_data){ .x = (X), .y = (Y) }, \
    xxsum.xxmacro__ret__ = xxsum.x + xxsum.y, \
    xxsum.xxmacro__ret__)

I have explained the details here (mainly section 4, for function-like macros):

Macros faking functions

  • The 1st line defines a macro that can be used to declare pseudoprototypes for macros.
  • The 2nd line uses that macro providing such a pseudoprototype. It defines "formal" parameters of the desired types. It contains, in order, the "return" type desired for the macro, the name of a struct where the parameters of the macro will be held, and finally the parameters (with types!) of the macro. I prefer call them pseudoparameters.
  • The 3rd line is an obligatory statement, which makes "real" the pseudoparameters. It declares a struct, that it is necessary to write. It defines a struct containing the "real" version of the pseudoparameters.
  • Finally, the macro itself it is defined, as a chained list of expressiones, separated by comma operators. The first operand is used to "load" the arguments of the macro into the "real" typed parameters. The last operand is the "returning value", which has also the desired type.

Observe that the compiler does right and transparent diagnostics of types.
(However, it is necessary to have some care with these constructs, as explained in the link).

Now, if you can collect all the sentences of your macro as a chain of function-calls separated by commas, then you can obtain function-like macro, as you desired.

Moreover, you can easily convert it into a real function, since the list of parameters is already defined. The type-checking was already done, so everything will work fine.
In the example above, you have to replace all the lines (except the first), by these other ones:

#define xxPseudoPrototype(RETTYPE, MACRODATA, ARGS) typedef struct { RETTYPE xxmacro__ret__; ARGS } MACRODATA

float SUM_intfloat(int x, float y) {                       /* (1) */
   xxPseudoPrototype(float, xxSUM_data, int x; float y; ); /* (2) */
   xxSUM_data xxsum;                                       /* (2) */
   return                                                  /* (3) */
     ( xxsum = (xxSUM_data){ .x = x, .y = y },             /* (4) (5) (6) */
         xxsum.xxmacro__ret__ = xxsum.x + xxsum.y,         /* (5) (6) */
         xxsum.xxmacro__ret__)                             /* (6) */ 
    ;                                                      /* (7) */
 }                                                         /* (8) */

The replacement will follow a sistematic procedure:

(1) Macro header turned into function header. Semicolons (;) are replaced by commas (,).
(2) Declaration lines are moved inside function body.
(3) The "return" word is added.
(4) Macro arguments X, Y, are replaced by function parameters x, y.
(5) All ending "\"s are removed.
(6) All intermediante calculations and function calls are left untouched.
(7) Semicolon added.
(8) Closing function body.

Problem: Although this approach solves your needs, note that the function has duplicated its list of parameters. This is not good: pseudoprototypes and duplicates have to be erased:

float SUM_intfloat(int x, float y) { 
   return  
     ( x + y )
    ;  
 }      
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文