真のDbC
先日までのエントリで、C++によるシンプルなDbCを理解することができたと思います。しかし、本当のDbCにはもっと強力な特徴があります。再現にあたり注意を払うべきDbCの特徴は以下です。

  • クラス不変条件は継承されます。派生クラスの不変条件は基底クラスの不変条件を暗黙的に呼び出します。
  • クラス階層のメンバ関数において、派生クラスのメンバ関数の事前条件は、すべてのオーバーライド関数の事前条件とORされます。事後条件はANDされます。

(日本語訳においてこちらのページを参考・引用)

言い換えれば、DbCはクラス会層に対して特別な動作を行います。クラス不変条件の継承を実現するために、基底クラスの不変条件を呼び出すように派生クラスを記述しなければなりません。例のページには次のようなコードが示されています:

void A::foo()
{
    
check_invariant(*this);
    ...
    
check_invariant(*this);
}

// B.h:

#include "A.h"

class B : public A {
    
public:
#ifdef DBG
    
virtual void invariant()
    {   ...
contracts...
       
A::invariant();
    }
#endif
       
void bar();
};


事前条件と事後に関して、メンバ関数内部で基底クラスの該当条件チェックを呼ぶコードを記述すべきです。 しかし、深いクラスでは、すべての条件を呼ばなければならないため、それは非常に複雑になるでしょう。

class B : A
{
protected:
    
#if DBC
    
int foo_preconditions() { ...Bpreconditions... }
    
void foo_postconditions() { ...Bpostconditions... }
    
#else
    
int foo_preconditions() { return 1; }
    
void foo_postconditions() { }
    
#endif

    
void foo_internal()
    {
    ...
implementation...
    }

public:
    
virtual void foo()
    {
    
assert(foo_preconditions() || A::foo_preconditions());
    
foo_internal();
    
A::foo_postconditions();
    
foo_postconditions();
    }
};


C++で事後条件を仕様(理想?)通りに実現することができるでしょうか? 恐らくそれは難しいでしょう。特に深い事前条件と事後条件はコードが複雑過ぎるため、使用に耐えないと思われます。しかし、実用(実益)レベルにおいて、これらのすべての仕様を実現させる必要があるかというとそうではなく、よく必要とする仕様だけを扱っても恩恵に預かれるはずです。本気で取り組むのであれば、プリプロセッサの開発が有効であると考えられます。
|