代码之家  ›  专栏  ›  技术社区  ›  Morten Siebuhr

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

  •  2
  • Morten Siebuhr  · 技术社区  · 15 年前

    我一直在黑客攻击一个程序,它本身在 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到20000个loc之间,大约有10年的模拟值得向后兼容。唉, 简单地 重命名任何东西一点都不简单…因为似乎没有任何简单的方法可以解决这个特定的问题,而不需要进行一些重大的重新设计(我认为我错过了 C 预处理器),我选择后退一步,看看还有什么其他选择。

    8 回复  |  直到 9 年前
        1
  •  3
  •   Salgar    15 年前

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

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

    正在扩展到

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

    随后将包含在您的声明中。 我不知道你想怎么解决这个问题,但我想说的是,改变你的变量名/定义一些更独特的名称来避免这个问题。

        2
  •  2
  •   messenger    15 年前

    只是一个疯狂的想法,但如果不是这个呢

    #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
    

    下面是一个在GCC4.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;
    }
    
        3
  •  1
  •   bdonlan    15 年前

    如果您使用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++之间不兼容的最大来源是缺少隐式空指针类型,我怀疑在你的输入片段中很少见。

        4
  •  0
  •   dls    15 年前

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

        5
  •  0
  •   Christoph    15 年前

    我会做的 position velocity 数组。您的定义如下

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

    因此,再也不可能进行重复的宏观扩张。

    将结构成员重命名为与 x 也可以,但我发现这里的数组更合理。

        6
  •  0
  •   Joakim Elofsson    15 年前

    如果定义必须是x和vx(with并不像您可能注意到的那样伟大),解决这个问题的一种方法是更改结构/类的成员 local->velocity.x 进入之内 local->velocity.x_ (或类似的东西)。

        7
  •  0
  •   caf    15 年前

    假设:

    • 不能更改结构成员的名称,以及
    • 不能更改定义的名称

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

    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,这样就不会有任何运行时开销。

    或者,如果“local”结构的定义由您决定,您可以将“position”和“member”指针指向union类型,并消除对强制转换的需要。

        8
  •  0
  •   bk1e    15 年前

    移除 #define 并更改代码生成器以生成适当的代码。这个 α定义 你所描述的似乎没有给你买任何东西,除了稍微短一点的标识符(或者有一些重要的事情你没有提到)。在代码生成器中展开变量,而不是在C预处理器中展开。

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