如何在编译时强制执行接口契约(在C中)?
背景: 我们正在为一个新的嵌入式系统的固件建模。目前固件是用UML建模的,但是UML建模工具的代码生成功能将不被使用 目标语言将是C(具体来说是C99) 低功耗(即性能、快速执行)和正确性很重要,但正确性是最高优先级,高于一切,包括代码大小和执行速度 在系统建模过程中,我们确定了一组定义良好的组件。每个组件都有自己的接口,许多组件与许多组件交互 模型中的大多数组件将是实时操作系统(RTOS)下的单个任务(线程),尽管有些组件只不过是库。任务之间完全通过消息传递/队列发布进行通信。与库的交互将以同步函数调用的形式进行 由于建议/建议可能取决于规模,我将提供一些信息。现在大概有12-15种成分,可能会增长到20种?不是100秒的组件。平均来说,每个组件与25%的其他组件交互 在中,有用于表示组件之间接口的端口/连接器,即一个组件提供另一个组件所需的。到目前为止还不错 问题在于:在许多情况下,我们不希望“组件A”访问所有“组件B”接口,即我们希望将组件A限制为组件B提供的接口的子集 问题/问题: 是否有一种系统的、相当直接的方法来强制执行(最好是在编译时)组件图上定义的接口契约 显然,编译时解决方案优于运行时解决方案(更早的检测、更好的性能、可能更小的代码) 例如,假设库组件“B”提供函数X()、Y()和Z(),但我只希望组件“a”能够调用函数Z(),而不是X()和Y()。类似地,即使组件“A”能够通过其消息队列接收和处理大量不同的消息,我们也不希望任何组件能够向任何组件发送任何消息 我能想到的最好办法是为每个组件接口提供不同的头文件,并且只公开(通过头文件)允许组件使用的接口部分。显然,这可能会导致大量头文件。这也意味着组件之间的消息传递不会直接通过OSAPI完成,而是通过函数调用完成,每个函数调用生成并发送一条特定的(允许的)消息。对于同步调用/库,只会公开API的允许子集 在本练习中,您可以假设人们行为良好。换句话说,不要担心人们直接作弊、剪切和粘贴函数原型,或者包括他们不允许的头文件。如果不允许的话,他们不会直接从“a”发信息到“B”,等等 也许有一种方法可以通过编译时断言强制执行契约。也许有一种更优雅的方法可以在运行时检查/强制执行此操作,即使它会产生一些开销如何在编译时强制执行接口契约(在C中)?,c,interface,uml,design-by-contract,contract,C,Interface,Uml,Design By Contract,Contract,背景: 我们正在为一个新的嵌入式系统的固件建模。目前固件是用UML建模的,但是UML建模工具的代码生成功能将不被使用 目标语言将是C(具体来说是C99) 低功耗(即性能、快速执行)和正确性很重要,但正确性是最高优先级,高于一切,包括代码大小和执行速度 在系统建模过程中,我们确定了一组定义良好的组件。每个组件都有自己的接口,许多组件与许多组件交互 模型中的大多数组件将是实时操作系统(RTOS)下的单个任务(线程),尽管有些组件只不过是库。任务之间完全通过消息传递/队列发布进行通信。与库的交互将以同
代码必须干净地编译&lint,因此“函数原型防火墙”方法是可以的,但似乎有一种更惯用的方法来实现这一点。标题的想法是合理的,但是,取决于组件之间的交错,将每个组件的接口划分为多个子类别,并使用它们自己的头文件,而不是为每个组件连接提供头文件,这可能会更简洁 子类别不一定是不相交的,但确保(通过预处理器指令)可以在不重新定义的情况下混合类别;这可以通过系统化的方式实现,方法是为每个类型或函数声明创建一个头文件,并使用其自己的包含保护,然后从这些原子块构建子类别头文件。
\ifdef FOO\u H_
#ifdef FOO_H_
/* I considered allowing you to include this multiple times (probably indirectly)
and have a new set of `#define`s switched on each time, but the interaction
between that and the FOO_H_ got confusing. I don't doubt that there is a good
way to accomplish that, but I decided not to worry with it right now. */
#warn foo.h included more than one time
#else /* FOO_H_ */
#include <message.h>
#ifdef FOO_COMPONENT_A
int foo_func1(int x);
static inline int foo_func2(message_t * msg) {
return msg_send(foo, msg);
}
...
#else /* FOO_COMPONENT_A */
/* Doing this will hopefully cause your compiler to spit out a message with
an error that will provide a hint as to why using this function name is
wrong. You might want to play around with your compiler (and maybe a few
others) to see if there is a better illegal code for the body of the
macros. */
#define foo_func1(x) ("foo_func1"=NULL)
#define foo_func2(x) ("foo_func2"=NULL)
...
#endif /* FOO_COMPONENT_A */
#ifdef FOO_COMPONENT_B
int foo_func3(int x);
#else /* FOO_COMPONENT_B */
#define foo_func3(x) ("foo_func3"=NULL)
#endif /* FOO_COMPONENT_B */
/*我考虑过允许您多次(可能是间接地)包含此内容
每次打开一组新的`#define`,但交互
在这和食物之间,我感到困惑。我毫不怀疑有一个好消息
实现这一目标的方法,但我决定现在不担心它*/
#warn foo.h包含多次
#else/*FOO_H_*/
#包括
#ifdef FOO_组件A
int foo_func1(int x);
静态内联int foo_func2(消息\u t*msg){
返回msg_send(foo,msg);
}
...
#else/*FOO_组件A*/
/*这样做将有希望使编译器用
一个错误,该错误将提示为什么不使用此函数名
错。您可能希望使用编译器(可能还有一些编译器)
其他)查看是否有更好的非法代码用于
宏*/
#定义foo_func1(x)(“foo_func1”=NULL)
#定义foo_func2(x)(“foo_func2”=NULL)
...
#endif/*FOO_组件A*/
#ifdef FOO_组件
int foo_func3(int x);
#else/*FOO_组件*/
#定义foo_func3(x)(“foo_func3”=NULL)
#endif/*FOO_组件*/
你应该考虑创建一个迷你语言和一个简单的工具来生成标题文件。
要在该答案中生成标题,如下所示(称之为
foo.comp
):
(并扩展示例以提供可由多个组件使用的接口):
这将很容易解析和生成头文件。如果您的接口(我特别怀疑消息传递可能是)是e
[COMPONENT_A]
int foo_func1(int x);
static inline int foo_func2(message_t * msg) {
return msg_send(foo, msg);
}
[COMPONENT_B]
int foo_func3(int x);
[COMPONENT_B, COMPONENT_C]
int foo_func4(void);