有任何C预处理器变量吗?

5

C中是否有类似于预处理变量的东西?它可以简化我的定义。

目前我有这样的代码:

typedef struct mystruct {
  int val1;
  int val2;
  int val3;
  int val4;
} MYSTRUCT;

typedef struct mysuperstruct {
  MYSTRUCT *base;
  int val;
} MYSUPERSTRUCT;


#define MY_OBJECT_BEGIN(name, val1, val2, val3, val4) \
  MYSTRUCT name##Base = { val1, val2, val3, val4 }; \
  MYSUPERSTRUCT * name##Objs = {

#define MY_OBJECT_VALUE(name, val) \
  { &(name##Base), val },

#define MY_OBJECT_END() \
  NULL \
};

它的使用方法如下:

MY_OBJECT_BEGIN(obj1, 1, 2, 3, 4)
MY_OBJECT_VALUE(obj1, 5)
MY_OBJECT_VALUE(obj1, 6)
MY_OBJECT_VALUE(obj1, 7)
MY_OBJECT_END()

生成的结果如下所示:
MYSTRUCT obj1Base = { 1, 2, 3, 4 };
MYSUPERSTRUCT * obj1Objs = {
  { &(obj1Base), 5 },
  { &(obj1Base), 6 },
  { &(obj1Base), 7 },
  NULL
}

很明显,重复使用对象名是多余的。我想将名称存储在MY_OBJECT_BEGIN定义中,作为某个预处理器变量,这样我就可以以下列方式使用它:
MY_OBJECT_BEGIN(obj1, 1, 2, 3, 4)
MY_OBJECT_VALUE(5)
MY_OBJECT_VALUE(6)
MY_OBJECT_VALUE(7)
MY_OBJECT_END()

标准的C预处理器提供一种实现这个的方法吗?

3
每当类似的情况出现,我总是喜欢提及X宏链接 - Oliver Charlesworth
1
最好的选择是:不要这样做,而是改进你的程序设计。因为如果你发现自己需要使用宏来进行奇特的操作,那么这肯定是一个糟糕的设计指标。 "X宏"通常作为最后的手段是有帮助的,当其他所有方法都失败了,并且它们主要用于维护旧代码,其中你无法修复设计问题。 - Lundin
3个回答

7

没有标准的C预处理器变量。正如Oli Charlesworth所建议的,如果您想仅使用标准的C语言,则使用X-Macros可能是最佳选择。如果确实有很多相关数据会涉及到多个文件,您需要使用类似GNU autogen的代码生成器。


+1 对于 X-Macros 和 autogen,我之前没有听说过,现在正在研究它们以便更广泛地使用。 - David Duncan
谢谢你们两个,Oli和ldav1s。X-Macros是一种有趣的技术,但当宏被定义在某种库中时,对于这个目的来说并不是很好。自动生成也不是我想要的。所以我可能必须继续使用我的冗余版本。但再次感谢你们,你们回答了我的问题。 - yman

2
在任意位置创建一个H文件,清除与您的宏相关的所有可能定义,例如:Original Answer。
#ifdef OBJECT
#    undef OBJECT
#endif

然后在项目中的某个地方为那个H文件定义一个宏:
(原始回答翻译成"最初的回答")
#define CLEAR "whatever/path/you/want/clear.h"

让你的宏使用OBJECT标签:
#define MY_OBJECT_BEGIN(val1, val2, val3, val4) \
  MYSTRUCT OBJECT##Base = { val1, val2, val3, val4 }; \
  MYSUPERSTRUCT * OBJECT##Objs = {

#define MY_OBJECT_VALUE(val) \
  { &(OBJECT##Base), val },

#define MY_OBJECT_END() \
  NULL \
};

使用方法如下:

像这样使用:

#include CLEAR
#define OBJECT obj1
MY_OBJECT_BEGIN(1, 2, 3, 4)
MY_OBJECT_VALUE(5)
MY_OBJECT_VALUE(6)
MY_OBJECT_VALUE(7)
MY_OBJECT_END()

#include CLEAR
#define OBJECT obj2
MY_OBJECT_BEGIN(1, 2, 3, 4)
MY_OBJECT_VALUE(5)
MY_OBJECT_VALUE(6)
MY_OBJECT_VALUE(7)
MY_OBJECT_END()

将清晰的代码放入一个单独的文件中,这样如果您将来需要多个定义来定义对象,只需要将清晰的代码修改添加到一个文件中即可。我知道这不是理想的解决方案,但这是您能做的最好的事情,因为没有办法使用宏参数来修改预处理器代码。"最初的回答"

同意,这个解决方案并不理想,但似乎可行(没有测试过,问题已经过时)。它可能对某些人有帮助。谢谢分享你的解决方案;-) - yman
@yman 是的,我知道这篇文章很老了,但当你搜索预处理器和变量时它是排名最高的。所以我想为那些从搜索引擎来的人回答它。我曾经使用预处理器完全实现了一个自己的C目标语言扩展,并将其推到了极限,这是我最终能够提出来以解决类似问题的最佳方法。 - Mecki

0

这是我能够想到的最接近通用技术,用于在预处理器定义中重复使用变量:

#include <stdio.h>

#define INIT_VALS(SIZE) unsigned int value[SIZE]; unsigned int i = 0;
#define DEFINE_VAL(NEW_VAL) value[i++] = NEW_VAL;
#define PRINT_VAL(INDEX) printf("%d\n", value[INDEX]);

int main()
{
    INIT_VALS(3)
    DEFINE_VAL(42)
    DEFINE_VAL(70)
    DEFINE_VAL(80)
    PRINT_VAL(0)
    PRINT_VAL(1)
    PRINT_VAL(2)

    // Output:
    // 42
    // 70
    // 80

    return 0;
}

我并不是在推销这个,但请务必仔细考虑是否有更好的解决方案。:)


谢谢你的建议David,但是这种方式数组不是由预处理器填充的,它需要一些CPU时间,因此对我来说不是一个理想的解决方案。 - yman
如果我错了,请有人纠正我,但我认为这是一个合理的期望,即使用典型的编译器优化,INIT_VALS()和DEFINE_VAL()的C代码将产生与您示例中显示的方法大致相同数量的CPU时间 - 也就是说,无论使用哪种方法(而不是确切的代码),都必须有指令来初始化值,并且编译器可能在任一情况下达到大致相同的程度。 - David Duncan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接