C ++アプリケーションでの契約原則による設計の実装を支援するライブラリはありますか?
特に、このような原理の使い方を備えた図書館を探しています。
C ++アプリケーションでの契約原則による設計の実装を支援するライブラリはありますか?
特に、このような原理の使い方を備えた図書館を探しています。
私は次の記事の教えに従いました:
私が最終的に適用したのは、ほとんどサメックのアプローチでした。REQUIRE、ENSURE、CHECK、およびINVARIANT(既存のマクロに基づくassert
)のマクロを作成するだけで非常に便利でした。もちろん、それは母国語のサポートほど良くはありませんが、とにかく、それはあなたがテクニックから実用的な価値のほとんどを得るのを許します。
ライブラリに関しては、アサーションメカニズムの重要な価値の1つはその単純さであるため、ライブラリを使用することにはお金がかからないと思います。
デバッグコードと本番コードの違いについては、「アサーションを本番コードに残す必要があるのはいつですか? 」を参照してください。。
最も単純ですか?
関数の開始時にステートメントをアサートして、要件をテストします。関数の最後にステートメントをアサートして、結果をテストします。
はい、それは粗雑で、大きなシステムではありませんが、そのシンプルさはそれを多用途でポータブルにします。
非仮想インターフェイスなどの一部のデザインパターンでは、特定のメソッドの事前条件/事後条件を簡単に記述できます。
#include <cassert>
class Car {
virtual bool engine_running_impl() = 0;
virtual void stop_impl() = 0;
virtual void start_impl() = 0;
public:
bool engine_running() {
return engine_running_impl();
}
void stop() {
assert(engine_running());
stop_impl();
assert(! engine_running());
}
void start()
{
assert(! engine_running());
start_impl();
assert(engine_running());
}
}
class CarImpl : public Car {
bool engine_running_impl() {
/* ... */
}
void stop_impl() {
/* ... */
}
void start_impl() {
/* ... */
}
}
C ++ 0x機能を使用してもかまわない場合は、ラムダとRAIIを使用して前提条件と事後条件を実装できます。
事後条件の簡単な例:
struct __call_on_destructor {
std::tr1::function<void()> _function;
template<class Func> inline __call_on_destructor(Func func) {
_function = func;
}
inline ~__call_on_destructor() {
_function();
}
};
#define on_scope_exit(function) \
__call_on_destructor PP_UNIQUE_LABEL(on_exit) (function)
#define ensures(expression) \
on_scope_exit([&] () { assert(expression); })
同様に、前提条件と不変条件を実装できます。コードは、非常に単純なC++0xコントラクトライブラリから取得されました。
これを試してください:Contract++。Boostに受け入れられました(ただし、まだ出荷されていません)。
要件、保険、不変条件を含む小さなc++ヘッダーがあります。400 loc未満であり、ニーズを満たす必要があります。dhc.hppの下にあります。エラーを便利な方法で報告し、defineを介してコンパイルできます。
#include <dbc.hpp>
class InvarTest {
public:
int a = 0;
int b = 9;
INVARIANT_BEGIN
Inv(RN(0,a,32));
Inv(RN(0,b,10));
INVARIANT_END
inline void changeMethod() {
Invariant(); // this runs the invariant block at the beginning and end of the method
a = 33;
}
};
int testFunc(int a, double d, int* ip) {
// RN = a in range 0 to 10, NaN = not a number, NN = not null
Rqr(RN(0,a,10), NaN(d), RN(0.0,d,1.0), NN(ip));
// Enr return the passed value
return Esr(RN(0.0,a+d,20.3));
}
void testFunc2(std::vector<int>& a, std::shared_ptr<int> sp) {
Rqr( SB(a,0), TE(a.size() % 12 == 0), NN(sp));
}
標準のASSERT/Q_ASSERTを使用しますが、特に外部テスト(NDEBUGなしでビルド)にそのような診断を残す場合は、「無効な」アサーションに注意してください。
C ++プロジェクトでのDBC実装(アサーションを使用)と「デバッグは常に有効」ポリシーに関する小さな話。
統合テストで次の状況に遭遇するまで、DBC実装としてかなり標準的なツール(ASSERT()/ Q_ASSERT())を使用していました。最新のビルドは開始直後に常に失敗していました。そのようなバージョンをリリースすることはあまり専門的ではありませんでした(内部QAの取り組みの1週間後)。
問題はどのように発生しましたか?
その結果、貧しい開発者はこのエラーのせいにされ(明らかにこのASSERTがなければクラッシュは発生しません)、統合テストを続行できるように修正プログラムをリリースする必要がありました。
まず第一に、失敗した状態を追跡するために統合テストでアサーションを有効にする必要があります(アサーションが多いほど良い)一方で、一部の「余分な」ASSERTが完全なソフトウェアスタックをクラッシュさせることを開発者に恐れさせたくありません。
私は、この問題に対するおそらく興味深いC ++ベースの解決策を見つけました:弱いasserions。アサーションが失敗したときにアプリケーション全体を停止するのではなく、後で分析するためにスタックトレースを記録して続行するという考え方です。クラッシュを恐れることなく、好きなだけ期待をチェックでき、統合からフィードバック(スタックトレース)を取得します。1回のプロセス実行で、1つだけではなく、多くの失敗したアサーションケースを分析に提供できます(abort()が呼び出されないため)。
このアイデアの実装(LD_PRELOADマジックを使用)については、ここで簡単に説明します:http: //blog.aplikacja.info/2011/10/assert-to-abort-or-not-to-abort-thats-the-question/