在
this example powered by Godbolt
,转载如下,
可以创建一个函数,它需要它的参数来满足某些条件
(i >= 0)
,
ptr != NULL
等等。
// EXPECT() : will trigger a warning if the condition is not guaranteed
#define EXPECT(c) (void)((c) ? (void)0 : never_reach())
// assume() : tell the compiler that `cond` is true
// note : only works for simple conditions, such as `i>=0`.
// Avoid complex conditions invoking functions.
#define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
// *********************************************************
// Function declaration
// *********************************************************
int positive_plus1_body(int v);
#define positive_plus1_preconditions(v) ((v)>=0) // Let's first define the preconditions.
// Name is long, because conditions must be unique to the function
// The inlined function express the contract, with both pre and post conditions.
// `inline` is essential, so that actual input values get verified,
// and result gets enriched with `assume` conditions.
// Problem is, the error is now located INTO the inlined function, not at the place where it's called.
// This makes it very hard to identify where the problem happens.
static inline int positive_plus1(int v) // Ideally, this function should also have additional attribute to not trigger warnings when it's not invoked
{
int r;
EXPECT(positive_plus1_preconditions(v)); // preconditions
r = positive_plus1_body(v);
assume(r > 0); // postconditions
return r;
}
这个
Godbolt
通过实例,可以用正确和错误的参数调用函数,并观察对编译诊断的影响。
如果调用者不遵守这些条件,编译器将生成一个警告。这对于立即检测条件冲突非常方便。通常的替代方法是
assert()
在函数体内部,但依赖运行时测试来检测潜在的条件冲突,这不太直接,也不太确定。
问题是,如果
inline
使用错误的参数调用函数时,编译器将查找条件冲突
里面
这个
内联
功能。这种诊断方法用处不大。相反,我们要读
内联
函数的参数不正确。
解决方法是改用宏。但是,宏会触发著名的多重求值问题,因为它的参数必须多次使用。这是危险的足以避免它。此外,封装
内联
函数可以用宏自动生成,但宏不能由另一个宏生成。
另一个解决办法是使用
statement expression
解决了多评价问题。但是,它是不可移植的,而且它仍然是一个宏,因此不能由另一个宏生成。
最好还是保持原样
内联
如果可能的话。但是在违反条件的地方生成正确的行号是非常重要的。
有没有一种方法可以在保持
内联
函数方法?