我目前正在开发一个功能,该功能提出了一个特别困难的问题。
我有两个宏:
#define FOO(A) do { /*...*/ } while(0)
#define FOO_END(A) do { /*...*/ } while(0)
FOO
的实际作用是(以非常特殊的方式)测试值,而FOO_END
将所有测试结果打印到标准输出中。详细内容在这里未公开,因为它不可修改。
其中每个都有多个输入(不只是“ A
”),但为简单起见,我将其限制为一个。
这两个宏的用法如下:依次调用多个FOO
宏,然后最后需要调用FOO_END
宏。我需要确保在第一个测试与最后一个测试之间没有调用任何“功能性”代码(因此所有值都是一致的)。
示例:
int main()
{
// stuff...
FOO(1);
FOO(2);
FOO_END(3);
// stuff...
FOO(4);
FOO_END(5);
// stuff...
return 0;
}
因此,以下代码必须无法编译:
int main()
{
FOO(1);
bar(); // a random function
FOO(2);
FOO_END(3);
return 0;
}
如何重新定义宏才能具有此功能?
FOO
和FOO_END
必须是宏。因为我需要用
其中的__LINE__
和__FILE__
。答案 0 :(得分:2)
如果使用开始宏,则可以在其中开始未完成的句子
#define FOO_START uselessFOOFunction(
#define FOO ); do { /*...*/ } while(0); uselessFOOFunction(
#define FOO_END ); do { /*...*/ } while(0)
static void uselessFOOFunction(){}
所以这将起作用:
int main()
{
// stuff...
FOO_START
FOO
FOO
FOO_END;
// stuff...
FOO_START
FOO
FOO_END;
// stuff...
return 0;
}
这将无法编译:
int main()
{
FOO_START
FOO
bar(); // a random function
FOO
FOO_END;
return 0;
}
答案 1 :(得分:1)
如果还从头开始添加FOO_START宏,则可能会生成代码,对于不正确的示例,该代码将因各种错误而无法编译。 不利的一面是,错误消息将与foo的使用无关,而是与宏扩展生成的错误语言构造有关。您也将无法使用局部变量。 因此,我想说这种形式是不可用的,但也许您可以根据您的用例进行修改-这类似于某些单元测试框架的实现方式。
这是一个简单的例子:
#include <iostream>
#define CONCAT(A,B) A ## B
#define ST_NAME(N) CONCAT(ST_, N)
#define NP_NAME(N) CONCAT(NP_, N)
#define np_NAME(N) CONCAT(np_, N)
#define MEM_NAME(N) CONCAT(mem_, N);
// NPs are helper structs that ensure that no data members are declared outside the FOO macros
#define NP_START struct NP_NAME(__LINE__) { int i;
#define NP_END } np_NAME(__LINE__); static_assert(sizeof(np_NAME(__LINE__)) == sizeof(int));
#define FOO_START struct { NP_START
#define FOO NP_END struct ST_NAME(__LINE__) { ST_NAME(__LINE__)() { std::cout << "hack!" << std::endl; } } MEM_NAME(__LINE__); NP_START
#define FOO_END NP_END } MEM_NAME(__LINE__);
void bar();
int main() {
int i;
FOO_START;
FOO;
i = 5; // error
int j = bar(); // error
FOO;
bar(); // error
FOO_END;
return 0;
}
这样,您将生成一个匿名的本地结构,该代码将由其构造函数执行。
编辑:修复了评论中提到的问题。 Static_assert需要C ++ 11或03的增强版。
答案 2 :(得分:0)
您不能使用建议的宏来执行此操作。无法确保宏语句之间不存在任何代码。不过,您可以更改方法。
创建一个宏DO_FOO
,它使用一个数字N
,这是执行FOO
的次数。 DO_FOO
会自动扩展到FOO
N
次,并扩展为一个FOO_END
。示例:
#define DO_FOO_IMPL_1 FOO; FOO_END
#define DO_FOO_IMPL_2 FOO; DO_FOO_IMPL_1;
#define DO_FOO_IMPL_3 FOO; DO_FOO_IMPL_2;
#define DO_FOO_IMPL_4 FOO; DO_FOO_IMPL_3;
// ... and so on, up to some maximum number N
#define DO_FOO(N) DO_FOO_IMPL_ ## N
然后您像这样使用它:
int main()
{
// stuff...
DO_FOO(2);
// stuff...
DO_FOO(1);
// stuff...
return 0;
}
答案 3 :(得分:0)
我的方法是这样的:如果您不希望人们在FOO和FOO_END的调用之间调用任意代码,则将所有FOO调用放入单个FOO_LIST调用中,该调用将全部调用,然后隐式调用FOO_END。这样,该规则由宏语法本身强制执行。
即代替:
FOO(1);
FOO(2);
FOO(3);
FOO_END;
让您的程序执行此操作:
FOO_LIST(1, 2, 3);
...,预处理器会将其扩展为FOO(1); FOO(2); FOO(3); FOO_END();
这是一个示例实现,在FOO_LIST中最多支持5个参数;如果您需要更多,可以扩展。
#include <stdio.h>
#define FOO(x) do {printf("foo(%i)!\n", x);} while(0)
#define FOO_END do {printf("foo_end!\n\n");} while(0)
// Support up to 5 FOO calls in a FOO_LIST, for now (can be extended as necessary)
#define FOO_LIST_1(f1) {FOO(f1); FOO_END;}
#define FOO_LIST_2(f1, f2) {FOO(f1); FOO(f2); FOO_END;}
#define FOO_LIST_3(f1, f2, f3) {FOO(f1); FOO(f2); FOO(f3); FOO_END;}
#define FOO_LIST_4(f1, f2, f3, f4) {FOO(f1); FOO(f2); FOO(f3); FOO(f4); FOO_END;}
#define FOO_LIST_5(f1, f2, f3, f4, f5) {FOO(f1); FOO(f2); FOO(f3); FOO(f4); FOO(f5); FOO_END;}
#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME
#define FOO_LIST(...) GET_MACRO(__VA_ARGS__, FOO_LIST_5, FOO_LIST_4, FOO_LIST_3, FOO_LIST_2, FOO_LIST_1)(__VA_ARGS__)
int main(int,char **)
{
printf("Some functional code here...\n");
FOO_LIST(1);
printf("Some more functional code here...\n");
FOO_LIST(1, 2);
printf("Some more functional code here...\n");
FOO_LIST(1, 2, 3);
return 0;
}