避免在 C 预处理器中重复替换
我一直在开发一个程序,它本身可以用C创建模拟程序。 用户指定顶层设计,该程序会插入小的 C 片段和大量的粘合代码(几千行)。
它通过 #defines 进行本地命名:
#define x local_x
#define vx local_vx
/* user code that uses x, ex */
vx = x / 2
#undef vx
#undef x
这个近似值扩展到以下内容:
local_vx = local_x / 2
但是如果我使用 local_*
变量的结构(优化后必须将 11 个变量传递给每个单个函数...):
#define x local->position.x
#define vx local->velocity.x
vx = x / 2
#undef vx
#undef x
它被扩展为
local->velocity.x = local->position.x
并且 - 这是问题 - 速度中的 x 再次扩展:
local->velocity.local->position.x = local->position.x
我不能在周围放置括号,因为不允许将其分配给变量((x) = 1
是非法的 C,不幸的是......)。 有什么提示吗?
更新:生成的模拟通常重量在 15 到 20.000 LOC 左右,并且有大约十年的模拟需要向后兼容。 唉,简单地重命名任何东西一点也不简单...因为似乎没有任何简单的方法可以在不进行重大重新设计的情况下解决这个特定问题(我以为我错过了一些C 预处理器的特殊性),我选择退后一步,看看还有什么其他选择。
I've been hacking on a program that itself creates simulation programs in C. The user specifies the top-level design, and this programs inserts small C-fragments and a helluvalot glue-code (a few thousand lines).
It does local naming by #defines
:
#define x local_x
#define vx local_vx
/* user code that uses x, ex */
vx = x / 2
#undef vx
#undef x
This approx expands to the following:
local_vx = local_x / 2
BUT if I use structs for the local_*
-variables (optimize away having to pass 11 variables to every single function...):
#define x local->position.x
#define vx local->velocity.x
vx = x / 2
#undef vx
#undef x
Which gets expanded to
local->velocity.x = local->position.x
And - here's the problem - the x
in the velocity gets expanded again:
local->velocity.local->position.x = local->position.x
I can't put parenthesis around, as it is not allowed to assign to the variables ((x) = 1
is illegal C, unfortunately...). Any hints?
Update: The generated simulations generally weigh in around 15 to 20.000 LOC, and there are roughly ten years worth of simulations to be backwards compatible with. Alas, simply renaming anything is not simple at all... As there does not seem to be any easy way to get around this particular problem without some major re-engineering (I thought I'd missed some particularities of the C pre-processor), I've chosen to take a step back and see what other options I have.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
它实际上并不是递归的,正在发生的事情是
被扩展到
稍后包含在您的语句中。
我不确定你想如何解决这个问题,但我想说将你的变量名称/#define 名称更改为更独特的名称以避免这种情况。
It's not actually being recursive, what is happening is that
is being expanded to
which is then included in your statement later on.
I'm not sure how you want to get around this but i'd say change your variable names/#define names to be something more unique to avoid this.
只是一个疯狂的想法,但是为什么
不把它命名为不同的名字呢? 就像
这是一个在 gcc 4.3.2 下运行良好的示例程序
Just a crazy idea but how about instead of this
Why not just name it something different? like
Here is an example program that runs fine under gcc 4.3.2
如果您使用 C++,您可能需要考虑使用引用:
由于您正在编写代码生成器,因此确保它们存在于正确的范围内应该不会太难:
作为额外的好处,附加集上面的内大括号允许用户代码在打算直接使用本地指针时隐藏 x。
如果您不使用 C++,请考虑这样做 - C 和 C++ 之间不兼容的最大根源是缺乏隐式 void 指针强制转换,我怀疑这在您的输入片段中很少见......
If you're using C++, you may want to consider using references instead:
Since you're writing a code generator, it shouldn't be too hard to make sure they exist for the right scope:
As an added bonus, the additional set of inner braces above allows the user code to shadow x if it intends to use the local pointer directly.
If you're not using C++, consider doing do - the biggest source of incompatibility between C and C++ would be the lack of implicit void pointer casts, which I would suspect are rare in your input fragments...
你用的 Morten 是什么编译器? 您的第一步可能是查看编译器参考手册,看看是否有任何可以更改有关预处理或递归替换级别的选项。
what compiler are you using Morten? Your first step could be to check out the compiler reference manual to see if there are any options you can change regarding levels of preprocessing or recursive substitution.
我会创建
position
和velocity
数组。 您的定义将如下所示,因此不再可能重复宏扩展。
将结构成员重命名为与 x 不同的名称也可以,但我实际上发现数组在这里更有意义。
I'd make
position
andvelocity
arrays. Your definitions would then look like thisso there's no longer a possibility for repeated macro expansion.
Renaming the structure member to something different from
x
would work as well, but I actually find that arrays make more sense here anyway.如果定义必须是 x 和 vx(with 不是那么好,正如您可能已经注意到的),解决此问题的一种方法是更改 struct/class 的成员
local->velocity.x
进入local->velocity.x_
(或类似的东西)。If the defines must be x and vx (with is not so great, as you might have noticed) one way to solve this is to change the member of the struct/class
local->velocity.x
intolocal->velocity.x_
(or something similar).假设:
那么一种方法是创建影子结构类型。 假设你的位置和速度成员是这种类型:
然后你创建一个除了成员名称之外相同的影子类型,以及一个包含两者的联合来绕过别名规则:
然后你的定义可以是
:一个黑客,但你受到很大的限制。 请注意, (&struct)->member 模式将优化为 struct.member,因此不会有任何运行时开销。
或者,如果“本地”结构的定义由您决定,您可以将“位置”和“成员”指针指向联合类型,并消除强制转换的需要。
Assuming that:
Then one way is to create a shadowing struct type. Say your position and velocity members are of this type:
Then you create a shadow type which is identical except for the names of the members, and a union containing both to get around aliasing rules:
and then your defines can be:
It's a bit of a hack, but you're pretty constrained. Note that the (&struct)->member pattern will be optimised down to struct.member so this won't have any runtime overhead.
Alternatively if the definition of the "local" struct is up to you, you could make "position" and "member" pointers to the union type, and remove the need for the casting.
删除#define 并更改代码生成器以生成适当的代码。 您描述的#define 似乎没有给您带来任何好处,除了稍短的标识符(或者有一些您没有提到的重要事情)。 在代码生成器中展开变量,而不是在 C 预处理器中。
关于与 10 多年模拟的兼容性,我希望代码生成器的原始输入文件已保存,以便您可以在必要时再次运行它(或者更好,生成代码是构建过程的一部分)。 如果这是某种交互式代码生成向导和/或开发人员编辑生成的代码,那么您已经陷入了痛苦的世界,我想知道您如何在第一个过程中对生成的代码进行任何重大更改放置(手动?后处理脚本?)。
Remove the
#define
s and change the code generator to generate the appropriate code. The#define
s you described don't seem to be buying you anything except slightly shorter identifiers (or there's something important going on that you didn't mention). Expand variables in the code generator, not in the C preprocessor.Regarding compatibility with 10+ years of simulations, I would hope that the original input files to the code generator have been saved so you can run it again if necessary (or even better, that generating the code is part of he build process). If this is some sort of interactive code-generating wizard and/or developers edit the generated code, you're already in a world of hurt, and I have to wonder how you're making any significant changes to the generated code in the first place (manually? post-processing script?).