0%

Contract in C++26

Reflection還沒通過,Contract就先進入下一版的C++標準當中了
如果要簡單講完Contract,大概就是C語言assert威力加強版

Before Contract

打開assert.h你大概可以看到類似這樣的程式碼

1
2
3
4
5
6
7
8
9
10
11
#ifdef  NDEBUG
# define assert(expr) (__ASSERT_VOID_CAST (0))
#else
# define assert(expr) \
((void) sizeof ((expr) ? 1 : 0), __extension__ ({ \
if (expr) \
; /* empty */ \
else \
__assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
}))
#endif

甚至知名的Open Source Project,也會搞自己的一套assert機制

1
2
3
4
5
6
7
8
9
10
11
12
#if SLANG_ASSERT_ENABLED
# define SLANG_ASSERT(cond) \
do { \
if (!(cond)) \
assertFailed(...); \
} while (false)
#else
# define SLANG_ASSERT(cond) \
do { \
(void)sizeof(cond); \
} while (false)
#endif

或是l
由於這個機制基於Macro,所以Macro Pollution是繞不開的問題
將Contract列如標準之後,有以下好處

  • 避免Macro Pollution
  • 更靈活的處理策略
  • 由於是Language本身的一部分,之後有更好的工具可供分析使用

How to Use

最簡單的方法就是將assert置換成`contract_assert

1
2
3
auto w = getWidget(); 
contract_assert(w.isValid());
processWidget(w);

基本行為就跟assert一致,不過可以透過Compiler控制Contract Semantics

Evaluation semantics

關於Contract,有四種不同的Evaluation方式

  • Ignore
  • Enforce
  • Observe
  • Quick-Enforce
    這邊就不喜說了,需要的話參考Reference的連結

Pre and Post

這個也跟以前function用assert檢查Input跟Output Result差不多

1
2
3
4
5
6
int f(const int x)  {
assert(x != 0 && x != -1);
int r = x + 1;
assert(r > x);
return r;
}

基本上沒什麼問題,只不過不能從FUnction signature中檢查,要深入Function body才知道問題出在哪
有了Contract之後,可以在函數宣告的地方加上prepost表示前置和後置條件

1
2
3
4
5
6
7
int f(const int x) 
pre(x != 0 && x != -1)
post(r : r > x);

int f(const int x) {
return x + 1;
}

就是把assert的使用情境細分

handle_contract_violation

有些時候,除了Compiler 內定的行為,我們需要自行對Contract做處理
這時候就是Global function handle_contract_violation發揮的時刻

1
2
3
4
5
6
7
8
9
10
11
12
// Try overriding the violation handler
void handle_contract_violation(std::contracts::contract_violation const& violation) {
if (violation.semantic() == std::contracts::evaluation_semantic::observe) {
std::cerr << violation.location().function_name() << ":"
<< violation.location().line()
<< ": observing violation my way: "
<< violation.comment()
<< std::endl;
return;
}
std::contracts::invoke_default_contract_violation_handler(violation);
}

最後一行表示回到內建的處理方式了

Status

目前MSVC還未實作,省略不計
gcc和clang的用法略有不同
目前gcc的使用方式:

1
-fcontracts  -fcontracts-nonattr -fcontract-evaluation-semantic=enforce

而clang則是:

1
$ -fcontracts -fcontract-evaluation-semantic=enforce

Reference

Contracts for C++ explained in 5 minutes
C++26启航:Safe C++的破晓时刻