如何确保(在编译时)连续调用两个宏?

时间:2018-07-26 13:40:41

标签: c++

我目前正在开发一个功能,该功能提出了一个特别困难的问题。

我有两个宏:

#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;
}

如何重新定义宏才能具有此功能?


约束

  • 必须在编译时看到错误。我已经有一个在运行时引发错误的版本,但这对我来说还不够。
  • FOOFOO_END 必须是宏。因为我需要用 其中的__LINE____FILE__
  • C ++ 11可以,但是我更喜欢C ++ 03解决方案。当然可以。
  • 如果可能的话,没有外部库。我在需要它的项目上的灵活性很低。但是,如果没有本机解决方案,我仍然很好奇。

4 个答案:

答案 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;
}