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

时间:2011-06-15 05:33:06

标签: c macros

我想在C文件上运行工具x并获取宏后代码。 (如果我们只能做更像宏的功能)。我知道gcc -E,但这也包含在一个大文件中。

基本上我想将一些C宏用于重复代码,但不希望最终代码包含任何宏,因为项目不赞成这些宏。

4 个答案:

答案 0 :(得分:3)

使用您选择的脚本语言,注释掉所有#include,然后运行gcc -E -Wp,-P,-C,-CC foo.c,然后取消注释#include。或者,您可以将#include替换为不以#开头的字符串...例如include#@include;可能性是无止境。使用@代替#的方法可以让您完全控制哪些预处理程序指令执行和不扩展...代码那些您不希望使用@扩展的代码,然后脚本只运行gcc -E,然后将@更改为#。但是,我认为最好采用其他方式,使用特殊标记(例如@)来指示您的可扩展宏。然后脚本会将前导#转换为其他内容(例如,HIDE#)并将标记(例如@)转换为#,运行gcc -E然后将HIDE#(或其他)转回#

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

答案 1 :(得分:2)

如何在#include列表后面的代码中放置分隔符,以便您可以手动删除包含文件扩展,但在运行gcc -E后保持宏扩展?

类似的东西:

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

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

答案 2 :(得分:1)

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

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

基本上这些是宏在项目中“皱眉”的原因。

你必须记住,inline仅仅是一个“建议”(即:函数可能实际上没有内联),而const将使用内存而不是一个常量字面(井) ,取决于编译器,良好的编译器将优化),但这将做两件事:

  1. 让您的代码遵守项目编码标准(这总是一件好事,至少在政治上,如果不一定是技术上的话)
  2. 代表您不需要额外的隐藏脚本或操作来保持代码的可重用性和可维护性(我假设您要使用宏以避免重复代码,对吧?)
  3. 因此,请记住这一点,作为一种选择。

答案 3 :(得分:0)

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

#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节,类似函数的宏):

Macros faking functions

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

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

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

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

#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) */

替换将遵循一个sistematic程序:

(1)宏标头变为功能标题。分号(;)由逗号(,)替换 (2)声明线在功能体内移动 (3)增加“返回”字样 (4)宏参数X,Y由函数参数x,y代替 (5)删除所有结尾的“\” (6)所有中间计算和函数调用保持不变 (7)添加分号 (8)关闭功能体。

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

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