有没有办法将C宏替换为正常的C代码

问题描述:

我想在C文件上运行工具x并获得后宏代码。 (如果我们只能像宏一样更好地工作)。我知道gcc -E,但那也包含在一个大文件中。有没有办法将C宏替换为正常的C代码

基本上我想使用一些C宏重复的代码,但不希望最后的代码包含任何的宏,因为它们经项目皱起了眉头。

+1

是不是'gcc -E'? – littleadv 2011-06-15 05:37:07

+0

不,因为我提到gcc -E不包含文件。我想要一个自动重新格式化后可用的结果文件。 – 2011-06-15 05:39:57

+1

我的意思是,你在问题中写了'gcc -F',我认为你的意思是'gcc -E' ... :-) – littleadv 2011-06-15 05:43:46

使用您选择的脚本语言,注释掉所有#include s,然后运行gcc -E -Wp,-P,-C,-CC foo.c,然后取消注释#include s。或者,您可以将#include替换为一些不以#开头的字符串,例如include#@include;可能性是无止境。使用@代替#的方法让您完全控制了该预处理指令做,没有得到扩大...代码你不想@扩大了的人,然后将脚本只是执行gcc -E,然后改变@#。不过,我认为最好反过来使用特殊标记(例如,@)来指示您的可展开宏。然后,脚本会将前导# s变成其他内容(例如,HIDE#),并将标记(例如@)变为#,运行gcc -E,然后将HIDE#(或其他)转回#

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

如何在#include列表后面的代码中添加分隔符,以便您可以手动删除包含文件扩展,但在运行gcc -E后宏扩展完好无损?

喜欢的东西:

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

我不知道,扩展宏,但不扩大#include个工具...

+0

有趣的想法。 – 2011-06-15 05:54:24

+0

这不起作用,因为它扩展了头文件中定义的所有宏,而不仅仅是本地宏。有一种更简单的方法...查看我的答案。 – 2011-06-15 08:05:03

+0

@Jim嗯......我假定宏定义在一个头文件中,但是又一次 - 你可以完全删除'#include'行,并在完成后将它们放回,并得到相同的结果,而不会扩大不需要的宏。我个人不喜欢命令行参数,越少越好,你必须在运行脚本之后编辑文件,所以并不重要。 – littleadv 2011-06-15 08:10:37

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

而不是有技巧将宏展开到项目源存储库 - 你有没有考虑使用const变量和inline函数作为替代?

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

你必须记住,inline仅仅是“建议”(即:该功能可能无法其实内联)和const会使用,而不是作为一个常量文字记忆(当然,依赖于编译器,编译好将优化),但会做两两件事:

  1. 保持你的代码遵守项目编码标准(这永远是一件好事,至少在政治上如果不一定是技术上的)
  2. 不需要额外的隐藏脚本或行为来代表您保持代码的可重用性和可维护性(我假设您想要使用宏以避免重复代码,对吗?)

因此k作为一种选择,也要考虑到这一点。

+0

+1从另一个线程的交换中提取肉,并将其转化为一个坚实的答案...做得很好......并超越了所述的问题,到了元问题......一个例子XY问题(http://www.google.com/search?q=X-Y+problem)。 – 2011-06-15 10:24:06

+0

很多这些都不是常数或者很容易在c函数中实现的东西。在函数调用任意数量的参数之前或之后添加代码非常困难,除了宏(使用#define M(f,a)... ret_val = func args ...“M(func_x,(argy, argz))“扩展为”func_x(argy,argz)“技巧 – 2011-06-22 07:20:07

作为一种可能的解决问题的方法:“写一个宏,然后将其丢弃,由等效功能替代”,你可以使用原型函数宏。他们有一些限制,必须小心使用。但它们的功能几乎与功能一样。

#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

  • 的第一线定义了可以用于声明pseudoprototypes宏宏为
  • 第二行使用该宏提供这样的假原型。它定义了所需类型的“正式”参数。它依次包含宏所需的“return”类型,宏的参数所在的结构的名称以及宏的参数(带有类型!)。我更喜欢打电话给他们假参数
  • 第三行是一个强制性声明,它使“真实”假参数。它声明了一个结构体,它有必要写。它定义了一个包含伪参数“真实”版本的结构。
  • 最后,将它自己的宏定义为表达式链表,用逗号分隔。第一个操作数用于将宏的参数“加载”到“真实”类型参数中。最后一个操作数是“返回值”,它也有所需的类型。

观察该编译器的类型权和透明的诊断。
(但是,有必要对这些结构有所关注,如链接所述)。

现在,如果您可以收集宏的所有语句作为由逗号分隔的函数调用链,那么您可以根据需要获取函数式宏。

此外,由于已经定义了参数列表,因此可以轻松将其转换为实际函数。类型检查已经完成,所以一切都会正常工作。 在上面的例子中,必须更换所有的行(第一个除外),由这些其他的:

#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) 
    ; 
}