【问题标题】:How to check if Parameter in Function like Macro is defined如何检查是否定义了宏等函数中的参数
【发布时间】:2021-12-08 07:56:56
【问题描述】:

我想检查PARA(s) 的参数是否已定义,或者,如果这是不可能的,如果它是一个整数。

#define PARA_STR(s) "<PARA"#s">"
#define PARA(s) PARA_STR(s)
#define ID 10

char str1[] = "Test" PARA(ID); // =Test<PARA10>
char str2[] = "Test2" PARA(ID2); // =Test2<PARAID2>

str2 的定义应该会导致错误。

我需要指出,该定义可能会在全局定义中使用

有没有办法做到这一点?

【问题讨论】:

  • PARA 宏可以出现的地方有什么限制吗?
  • 它总是用于字符串初始化。本地和全球。
  • 恐怕,宏无法做到这一点。
  • “定义”是什么意思?定义了那个宏ID?或者标识符被定义为enum { ID = 10; }
  • @tstanisl 目前我们使用#define ID 10enum { ID = 10, } 是可能的。

标签: c macros c-preprocessor


【解决方案1】:

很难找到可靠的解决方案。 如果PARA 用于char* 的初始化,那么PARA 可以扩展为PARA_STR(s),后跟+ 和一个依赖于s 的常量表达式0。 例如:

#define PARA_STR(s) "<PARA"#s">"
#define PARA_CONST_EXPR(s) (1 ? 0 : ((void)(s),0))
#define PARA(s) PARA_STR(s) + PARA_CONST_EXPR(s)

代码:

#define ID 10

char *str1 = "Test" PARA(ID);
char *str2 = "Test2" PARA(ID2);

它会产生错误信息:

prog.c:22:27: error: ‘ID2’ undeclared here (not in a function); did you mean ‘ID’?
   22 | char *str2 = "Test2" PARA(ID2);

它适用于任何已定义的ID,例如枚举甚至结构。

enum { ID = 5 };
struct { int _; } ID;

char[] 的初始化程序的情况更复杂,更不可靠。 您可以尝试通过添加, 并插入某个指针虚拟对象的新声明来劫持变量的声明。该对象将使用常量0(又名NULL)初始化,该常量表达式由依赖于s(检测未定义标识符)和虚拟变量本身的常量表达式组成,以消除有关未使用变量的警告。 虚拟对象的名称必须是唯一的。 它可以借助__LINE__ 宏或非标准但流行的__COUNTER__ 宏来形成。

经过一些实验我发现:

#define PARA_STR(s) "<PARA"#s">"
#define PARA_CONST_EXPR(s,var) (1 ? 0 : ((void)(s),(void*)&var))
#define CONCAT_(a,b) a ## b
#define CONCAT(a,b) CONCAT_(a,b)
#define PARA_DECL(s, var) *var = PARA_CONST_EXPR(s,var)
#define PARA_UNIQ_VAR CONCAT(para__, __LINE__)
#define PARA(s) PARA_STR(s), PARA_DECL(s, PARA_UNIQ_VAR)


int ID;

char str1[] = "Test" PARA(ID);
char str2[] = "Test2" PARA(ID2); // detected

char *strp1 = "Test" PARA(ID);
char *strp2 = "Test" PARA(ID2); // detected

int main() {
    char str3[] = "Test3" PARA(main);
    char str4[] = "Test3" PARA(main2); // detected
}

只要在初始化程序的末尾使用PARA,它就可以很好地工作。

【讨论】:

  • 感谢您的关注!不幸的是,PARA 通常不会在初始化程序的末尾使用。我仍然尝试理解表达式((void)(s),0))。你知道解释劫持声明的资源吗?
  • @Noetzold, ((void)(s),0)(s, 0) 相同。逗号运算符的工作原理是丢弃第一个值,只返回第二个值。强制转换为 void 用于在警告未使用值时使编译器静音。这个技巧允许创建一个使用 s 但其值不依赖于 s 的表达式
  • @Noetzold,我发明了“劫持声明”一词。基本上,这意味着当宏X 在声明中扩展时,即int a = X; 然后X 可以通过扩展为42, b = 1 添加一个额外的变量。所以int a = X; -> int a = 42, b = 1;。这意味着声明上下文被X劫持,让声明额外的变量。
  • 谢谢你的解释,我明白了。现在我明白为什么它只在 PARA 结束时才有效。
猜你喜欢
  • 1970-01-01
  • 2021-08-26
  • 2012-01-25
  • 1970-01-01
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多