避免在C预处理器中重复更换

时间:2009-07-28 16:44:38

标签: c c-preprocessor

我一直在攻击一个本身在 C 中创建模拟程序的程序。用户指定顶级设计,此程序插入小C片段和helluvalot胶码(几千行)。

它按#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 预处理器的特殊性,我选择退后一步,看看我还有其他选择。

8 个答案:

答案 0 :(得分:3)

它实际上不是递归的,发生的是

#define x local->position.x
#define vx local->velocity.x

正在扩展到

#define x local->position.x
#define vx local->velocity.local->position.x

稍后会在您的陈述中包含。 我不确定你是如何解决这个问题的,但我会说改变你的变量名称/ #define名称是更独特的,以避免这种情况。

答案 1 :(得分:2)

只是一个疯狂的想法,但如何而不是这个

#define x local->position.x
#define vx local->velocity.x
vx = x / 2
#undef vx
#undef x

为什么不把它命名为不同的东西?像

#define x local->position.val
#define vx local->velocity.val
vx = x / 2
#undef vx
#undef x

这是一个在gcc 4.3.2下运行良好的示例程序

int main(int argc, char *argv[])
{
   typedef struct
    {
        unsigned char val;
    } Value;

    typedef struct
    {
        Value position;
        Value velocity;
    } Holder;

    Holder temp;
    Holder* local = &temp;
    #define x local->position.val
    #define vx local->velocity.val
    vx = x / 2;
    #undef vx
    #undef x

    return 0;
}

答案 2 :(得分:1)

如果您使用的是C ++,则可能需要考虑使用引用:

int &x = local->position.x;

由于您正在编写代码生成器,因此确保它们存在于正确的范围内应该不会太难:

{
    int &x = local->position.x;
    int &y = local->position.y;
    int &vx = local->velocity.x;
    int &vy = local->velocity.y;
    {
#line user.input 1234
        // user code
#line output.c 4567
    }
}

作为一个额外的好处,如果打算直接使用本地指针,上面附加的内括号允许用户代码遮蔽x。

如果您不使用C ++,请考虑这样做 - C和C ++之间不兼容的最大原因是缺少隐式void指针转换,我怀疑在您的输入片段中很少...

答案 3 :(得分:0)

你在使用Morten的编译器是什么?您的第一步可能是查看编译器参考手册,看看是否有任何可以更改预处理或递归替换级别的选项。

答案 4 :(得分:0)

如果定义必须是x和vx(并不是很好,正如您可能已经注意到的那样),解决此问题的一种方法是将struct / class local->velocity.x的成员更改为local->velocity.x_ (或类似的东西)。

答案 5 :(得分:0)

我会制作positionvelocity数组。您的定义将如下所示

#define x local->position[0]
#define vx local->velocity[0]

因此不再有可能重复进行宏扩展。

将结构成员重命名为与x不同的东西也可以,但实际上我发现数组在这里更有意义。

答案 6 :(得分:0)

假设:

  • 您无法更改结构成员的名称和
  • 您无法更改定义的名称

然后一种方法是创建一个阴影结构类型。假设您的位置和速度成员属于这种类型:

struct vector {
  double x;
  double y;
};

然后你创建一个除了成员名称相同的阴影类型,以及一个包含两者来解决别名规则的联合:

struct _vector {
  double _x;
  double _y;
};

union _u_vector {
    struct vector _v1;
    struct _vector _v2;
};

然后您的定义可以是:

#define x ((struct _vector *)(union _u_vector *)(&local->position))->_x
#define vx ((struct _vector *)(union _u_vector *)(&local->velocity))->_x

这有点像黑客,但你很受限制。请注意,(& struct) - >成员模式将优化到struct.member,因此不会有任何运行时开销。

或者,如果“局部”结构的定义由您决定,您可以将“位置”和“成员”指针指向联合类型,并消除对投射的需要。

答案 7 :(得分:0)

删除#define并更改代码生成器以生成相应的代码。你描述的#define似乎没有给你买任何东西,除了标识符稍短(或者你没有提到的重要内容)。在代码生成器中展开变量,而不是在C预处理器中展开。

关于与10年以上模拟的兼容性,我希望保存代码生成器的原始输入文件,以便在必要时再次运行它(或者甚至更好,生成代码是构建过程的一部分)。如果这是某种交互式代码生成向导和/或开发人员编辑生成的代码,那么您已经处于一个受到伤害的世界,我不得不想知道您是如何对第一个生成的代码进行任何重大更改的。放置(手动?后处理脚本?)。