c - 用于识别 ANSI C 中损坏的自动生成接口(interface)层的静态断言

标签 c compiler-errors c89 static-assert

问题

我试图找到静态(编译时)断言,以确保(尽可能好地)下面的事情。当我在自动代码生成上下文中使用它们时(请参阅下面的“背景”),它们不必整洁,只需要中断编译,最好是零开销。不过,我们欢迎优雅的变体。

应检查以下内容:

类型标识

typedef T T1;
typedef T T2;
typedef X T3;
T1 a;
T2 b;
T3 c;

SA_M1(T1,T2); /* compilation */
SA_M1(T1,T3); /* compilation error */

SA_M2(a,b); /* compilation */
SA_M2(a,c); /* compilation error */

其中 XT 是 C 类型(包括结构化、聚合、对象指针,不是那么重要的函数指针)。再次注意,一组部分成功的解决方案也有帮助。

我假设的一些解决方案将部分起作用:

  • 比较尺寸
  • 通过尝试取消引用来检查该类型是否为声明的指针。
  • 对于无符号整数:将略微转换为大的值与预期的环绕值进行比较。
  • 对于 float ,将 double 精确表示值与类型转换值进行比较(希望平台特定的舍入操作最好)

B 变量具有全局作用域

目前我的解决方案是简单地生成一个静态函数,它试图获取对全局变量的引用假设 X 是一个全局变量:

static void SA_IsGlobal_X() {(void) (&X == NULL); /* Dummy Operation */}

C 函数具有正确数量的参数

我还不知道。

D 如果一个函数的原型(prototype)符合预期

我还不知道。

E 如果函数或宏参数是编译时常量(

这里针对宏讨论这个问题:

Macro for use in expression while enforcing its arguments to be compile time constants

对于函数,包装器宏可以做到。

Z 考虑到下面的“背景”部分,您可能想检查的其他事项

首选的是可以使用 C89 完成的答案,在运行时、堆栈和(对于大多数编译器)代码大小方面成本为零。由于检查将自动生成,因此可读性并不那么重要,但我喜欢尽可能将检查放在静态函数中。


背景:

我想提供 C 函数和接口(interface)生成器,以允许它们顺利地集成到不同的 C 框架中(即将推出 C++)。界面生成器的用户然后仅指定输入来自何处,以及哪些输出应该去往何处。选项至少是:

  • RAW(已实现 - 应该使用)
  • 来自接口(interface)函数参数,它是一种据说与我的输入/输出相同的类型(可能是结构或数组元素的字段)
  • 来自 getter/setter 函数
  • 来自全局变量
  • 使用编译时间常数

我会:

  • 要求非常详细的接口(interface)规范(包括规范错误)
  • 使用解析器检查 typedef 和声明(包括工具错误和我的工具使用错误)

但这发生在生成时。除此之外:如果用户更改环境或采用我的函数的新主要版本(这可以通过宏检查版本来解决),而无需再次运行界面生成器,我希望在编译时有最后一道防线.

几代人的结果代码可能接近最坏的情况:

#include "IFMyFunc.h" /* contains all user headers for the target framework(s) */
#include "MyFunc.h"

RetType IFMYFunc(const T1 a, const struct T2 * const s, T3 * const c)
{

   /* CHECK INTERFACE */
   CheckIFMyFunc();

   /* get d over a worst case parametrized getter function */
   const MyD_type d = getD(s->dInfo);

   /* do horrible call by value and reference stuff, f and g are global vars */
   c.c1 = MyFunc(a,s->b,c.c1,d,f,&(c->c2), &e,&g);

   set(e);

   /* return something by return value */ 
   return e;
}

(我很确定我会限制组合)。

static void CheckIFMyFunc(void)
{
    /* many many compile time checks of types and specifications */
}

或者我将提供一段直接注入(inject)的代码(本地 block )——这是一个可怕的架构,但如果我们不能足够快地放弃一些由一些遗留脚本支持的框架工作,则可能是必要的。

最佳答案

对于A,会提议:

#define SA_M1(A, B)            \
        do {                   \
                A ___a;        \
                B ___b = ___a; \
                (void)___b;    \
        } while (0)

对于 D(我会说 C 已经由 D 完成)

typedef int (*myproto)(int a, char **c);

#define FN_SA(Ref, Challenger) \
        do { \
                Ref ___f = Challenger; \
                (void) ___f; \
        } while (0)

void test(int argc, char **argv);

int main(int argc, char **argv)
{
        FN_SA(myproto, main);
        FN_SA(myproto, test); /* Does not compile */
        return 0;
}

然而,void * 仍然存在一些问题: 在 C 中,任何指针都可以转换为 void *,这可能会使 A 的解决方案在某些情况下失败....

顺便说一句,如果您打算在此期间使用 C++,您可以只使用 C++ 模板等来完成此测试。恕我直言,会更加干净可靠。

关于c - 用于识别 ANSI C 中损坏的自动生成接口(interface)层的静态断言,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33582185/

相关文章:

c - 如何找到我当前的编译器标准,例如是否是 C90 等

C - 调用多个 fork() 调用的问题

python - pip安装pyaudio错误cl.exe失败

c - 将越界索引与从足够大的数组转换而来的较小数组一起使用是否安全?

html - 脚本已完成,但返回的值不是受支持的返回类型?

android - love2d编译器行为的差异

c - 如何编写一个 vfprintf 包装器,为格式说明符添加前缀并将新格式说明符传递给 C89 中的 vfprintf?

c - C 中的 Pthreads。简单的示例不起作用

c++ - 使用位域输出结构大小

c - 带有 Rcpp 的 .c 和 .cpp 文件的 R 包