2012年8月26日日曜日

インポート (import)

今日は、インポートの実現方法について解説する。 と言っても、結構ばかばかしい。


これまでの投稿で示した例題では無名パッケージのクラスだったが、命名規則 (naming convention)で示しているように softies projectではパッケージの概念を導入している。
たとえば、単体テストフレームワーク CUnitに Assertクラスがあるが、もしテスト対象で既に別の Assertという名前を使用していると、シンボルの衝突が発生してしまう。
そのため、CUnitは衝突が起こりにくいよう、lwd_unitパッケージに Assertクラスを置いている。

完全修飾名 (fully qualified name)

インポートを使わない場合の Assertクラスの使用例をつぎに示す。(example of using Assert class without import.)
/* 25.Aug.2012 kei */

#include <lwd/unit/Assert.h>

int main() {
        int x = 0;
        lwd_unit_Assert_equalsInt("x equals 0.", 0, x);

        return 0;
}
この様に、長い名前で equalsIntを呼び出さなければならない。
シンボルの衝突は置きにくいが、冗長である。

インポート (import)

インポートを使用した場合の Assertクラスの使用例をつぎに示す。(example of using Assert class with import.)
/* 25.Aug.2012 kei */

#define import_lwd_unit_Assert

#include <lwd/unit/Assert.h>

int main() {
        int x = 0;
        Assert_equalsInt("x equals 0.", 0, x);

        return 0;
}
import_lwd_unit_Assertを定義するだけで、パッケージ名を省き、クラス名と関数名だけで関数呼び出しができるようになる。

仕掛け (mechanism)

Assertクラスのヘッダには次のような記述が含まれており、import_lwd_unit_Assertを定義することにより、Assertクラスの関数名などの長い名前の省略形がマクロ定義されるようになっている。
#ifdef import_lwd_unit_Assert
        #define Assert_equalsInt        lwd_unit_Assert_equalsInt
        #define Assert_equalsLongLong   lwd_unit_Assert_equalsLongLong
        #define Assert_equalsDouble     lwd_unit_Assert_equalsDouble
        #define Assert_equalsString     lwd_unit_Assert_equalsString
        #define Assert_equals           lwd_unit_Assert_equals
        #define Assert_null             lwd_unit_Assert_null
        #define Assert_notNull          lwd_unit_Assert_notNull
        #define Assert_false            lwd_unit_Assert_false
        #define Assert_true             lwd_unit_Assert_true
        #define Assert_fail             lwd_unit_Assert_fail
#endif /* import_lwd_unit_Assert */
仕掛けは単純だが、これがあるかどうかで使いやすさが違ってくる。
通常はインポートを使用し、名前が衝突する場合にのみ FQNで記述すればよい。
なお、クラスのメンバが追加されたり名称に変更があった場合など、忘れずに省略形の修正も行わなければならない。

原発を灰色にするのは…

2012年8月24日金曜日

例外機構 その2 (exception mechanism 2)

(about limitation of softies type exception mechanism)
前回投稿した、例外機構 (exception mechanism)では、その冒頭で「使用上の制約もあることにも留意。」と書いたが、それについてのまとめ。

final節を省略できない (cannot omit finally clause)

finalは GCCの拡張機構であるネスト関数(GCC nested function)を前方参照で呼び出しているため、存在することを前提として try処理を始める。
したがって、final節を省略することはできない。
final節を記述しなかった場合、つぎのような lwd_Environment_finally定義されていないというコンパイルエラーが発生する。
ExceptionExampleTest.c: In function ‘testExceptionWithoutFinal’:
ExceptionExampleTest.c:51:1: error: expected declaration or statement at end of input
ExceptionExampleTest.c:51:1: error: expected declaration or statement at end of input
ExceptionExampleTest.c:42:2: error: nested function ‘lwd_Environment_finally’
 declared but never defined

throwsの記述ができない (throws clause cannot describe)

Javaではチェック例外と非チェック例外があり、チェック例外をスローする関数に throwsを付けることにより、catchを強制することができる。
C++では例外仕様(exception specifications)を記述して、catchするように促すことができる。
しかし、Cではこのような仕掛けはできそうもない。

1つの関数に複数個並べることができない (multiple try)

finallyはネスト関数名 lwd_Environment_finallyで受けているため1つの関数の中で2つ以上の finallyが書けず、2個以上の tryを書くことができない。
void testDoubleTry() {
        try {
                throw(new_TestException("test 1"));
        } catch (TestException, e) {
                printf("TestException caught, %s\n", e->message);
        } finally {
                printf("finally 1\n");
        }

        try {
                throw(new_TestException("test 2"));
        } catch (TestException, e) {
                printf("TestException caught, %s\n", e->message);
        } finally {
                printf("finally 2\n");
        }
}

このファイルをビルドすると、
$ make
cc -g -finstrument-functions -I.  -I ../../core/include -I ../../experimental/include
 -I ../../io/include -I ../../lang/include -I ../../message/include -I ../../unit/
include -I ../../util/include -I ../../x11/include  -c -o ExceptionExampleTest.o
 ExceptionExampleTest.c
$
ビルドエラーにはならがいが、実行してみると、
[CUnit] ./ExceptionExampleTest: testDoubleTry
TestException caught, test 1
finally 2
TestException caught, test 2
finally 2

どうやら、GCCは1つの関数内に同じ名前のネスト関数が含まれていてもエラーにしないが、後にある関数を呼び出すようだ。
対策としては、つぎのように各々の tryを { }でくくればよい。(ちょっと格好悪い)
void testDoubleTry() {
        { try {
                throw(new_TestException("test 1"));
        } catch (TestException, e) {
                printf("TestException caught, %s\n", e->message);
        } finally {
                printf("finally 1\n");
        }}

        { try {
                throw(new_TestException("test 2"));
        } catch (TestException, e) {
                printf("TestException caught, %s\n", e->message);
        } finally {
                printf("finally 2\n");
        }}
}

つぎのように期待した通りの結果になる。
[CUnit] ./ExceptionExampleTest: testDoubleTry
TestException caught, test 1
finally 1
TestException caught, test 2
finally 2
括弧を付けていないときにエラーにならないのは問題なので、tryごとにダミーのローカル変数かなにかを置けばポカ避けにはなるだろう。

tryのネストはできる (nested try)

tryの中に tryがあるときは問題ない。
void testNestedTry() {
        try {
                try {
                        throw(new_TestException("test inner"));
                } catch (TestException, e) {
                        printf("TestException caught inner, %s\n", e->message);
                } finally {
                        printf("finally inner\n");
                }
                throw(new_TestException("test outer"));
        } catch (TestException, e) {
                printf("TestException caught outer, %s\n", e->message);
        } finally {
                printf("finally outer\n");
        }
}

実行結果は、
[CUnit] ./ExceptionExampleTest: testNestedTry
TestException caught inner, test inner
finally inner
TestException caught outer, test outer
finally outer

catchの中のtryは駄目 (try in catch clause)

void testTryInCatch() {
        try {
                throw(new_TestException("test outer"));
        } catch (TestException, e) {
                printf("TestException caught outer, %s\n", e->message);
                try {
                        throw(new_TestException("test inner"));
                } catch (TestException, e) {
                        printf("TestException caught inner, %s\n", e->message);
                } finally {
                        printf("finally inner\n");
                }
        } finally {
                printf("finally outer\n");
        }
}
catchの中の tryは、外部および内部の finallyの関数呼び出しがいづれも内部の finally関数を参照してしまうためうまくいかない。
[CUnit] ./ExceptionExampleTest: testTryInCatch
TestException caught outer, test outer
finally inner
TestException caught inner, test inner
finally inner
finally outer
しかも、外部の finallyも実行してしまってるし。



貧しくて少し照れちゃうけど

2012年8月21日火曜日

例外機構 (exception mechanism)

オブジェクト指向とは直接関係ないが、言語 Cで例外の実験をしてみたいと思う。
(実験用のため実用にするときには十分な検討が必要。使用上の制約もあることにも留意。)

たとえば、次のようなコードを動かしたい。
try {
                throw(new_TestException("test"));
        } catch (TestException, e) {
                printf("TestException caught, %s\n", e->message);
        } finally {
                printf("finally\n");
        }
構造はつぎのようになる。
try
        主成功シナリオ (main success scenario)
catch
        代替シナリオ (alternative scenario extensions)
finally
        tryブロック終了処理 (always executes when the try block exits)
C++には finallyが無くて困ったことがあるので、なんとか実現したい。
catch節の TestExceptionの後ろに付いている ',' (comma)は間違って付けた鼻くそではない。
Cに無い構文 try - catch - finallyのためにマクロを使おうと思うが、catchは引数つきマクロにするため、型名とインスタンス名の2つを引数を区切る ','が必要となる。

マクロ定義 (macro definition)

例外機構のためのヘッダファイル throw.hを記述してみた。
/*
 * 9.11.12.13.29.31.Dec.2011 kei
 * 15.16.21.Aug.2012
 */

#ifndef _Included_lwd_throw
#define _Included_lwd_throw

#include <setjmp.h>
#include <lwd/Environment.h>

#define lwd_try \
        auto void lwd_Environment_finally(); \
        { \
        struct lwd_Environment lwd_exception; \
        lwd_Environment_push(&lwd_exception); \
        int lwd_jumpStatus = setjmp(lwd_exception.buffer); \
        if (lwd_jumpStatus == 0) {

#define lwd_catch(T, V) \
        } else if (strcmp(lwd_Throwable_getType(lwd_exception.throwable), #T) \
        == 0) { \
                lwd_boolean lwd_Environment_catchClause = lwd_true; \
                lwd_Environment_pop(&lwd_exception); \
                if (!lwd_Environment_isEmpty() \
                && (lwd_exception.throwable != NULL)) \
                        lwd_Environment_setThrowable(&lwd_exception); \
                T V = (T)lwd_exception.throwable; \

#define lwd_finally \
        } else { \
                lwd_Environment_pop(&lwd_exception); \
                if (!lwd_Environment_isEmpty() \
                && (lwd_exception.throwable != NULL)) \
                        lwd_Environment_setThrowable(&lwd_exception); \
                lwd_throw(lwd_exception.throwable); \
        }} \
        lwd_Environment_finally(); \
        void lwd_Environment_finally()

#define lwd_throw(O) \
        if (lwd_Environment_catchClause) \
                lwd_Environment_finally(); \
        if (lwd_Environment_isEmpty()) \
                lwd_Environment_abort((lwd_Throwable)O); \
        lwd_Environment_throw((lwd_Throwable)O)

#endif /* _Included_lwd_throw */

Cで try - catchを実現するほかの多くの例と同様、setjmp, longjmpを使用する。
try, catch, finally, throwは各々 lwd_try, lwd_catch, lwd_finally, lwd_throwとして定義している。
これを、lwd.hファイルの中で置き換えている。
#define try     lwd_try
        #define catch   lwd_catch
        #define finally lwd_finally
        #define throw   lwd_throw

finallyを実現するために、GCC拡張の ローカル関数(関数の中で定義される関数)を使用している。
auto void lwd_Environment_finally()がそれにあたる。
catchの中では、catchClause変数を trueとすることで、外にあるときと throwの動作を切り替えている。
throwが呼ばれたときに直ちに上位階層に longjmpしてしまっては finallyが実行されなくなる。catchClauseが trueの場合は longjmpに先立って finally関数が呼ばれる。ただしこのfinally関数内で throwが行われると、そちらが優先されてしまう。
try 以外のところで throwを実行すると、abortする。

環境 (environment)

setjmp, longjmpのためのコンテキストを保存するために、Environmentクラスを使用している。

Environment.hはつぎのとおり。
/*
 * 9.12.13.29.31.Dec.2011 kei
 * 13.Jan.2012
 * 21.Aug.2012
 */

#ifndef _Included_lwd_Environment
#define _Included_lwd_Environment

#include <setjmp.h>

typedef struct lwd_Environment* lwd_Environment;

#include <lwd/EnvironmentSPI.h>
#include <lwd/Throwable.h>
#include <lwd/types.h>

struct lwd_Environment {
        jmp_buf buffer;
        lwd_Environment chain;
        lwd_Throwable throwable;
};

extern void lwd_Environment_push(lwd_Environment environment);

extern void lwd_Environment_pop(lwd_Environment environment);

extern lwd_boolean lwd_Environment_isEmpty();

extern void lwd_Environment_setThrowable(lwd_Environment environment);

extern const lwd_boolean lwd_Environment_catchClause;

extern void lwd_Environment_throw(lwd_Throwable throwable);

extern void lwd_Environment_finally();

extern void lwd_Environment_abort(lwd_Throwable throwable);

extern void lwd_Environment_setSPI(lwd_EnvironmentSPI spi);

#endif /* _Included_lwd_Environment */

bufferがレジスタなどのコンテキストを保存するための領域。
chainはtryの中でさらに tryを使用する場合にスタック構造をつくるためのもの。
throwableは実際にスローされた例外。
push, pop, isEmptyは tryが多重に呼ばれたときの環境を操作するための関数。
catchClauseは tryの中にいるかどうかを判断するために使用される名称、外部は常に falseになる。
setSPIは、push, pop等の動作を変更したい場合に使用する。

関数定義の Environment.cはつぎのとおり。
/*
 * 9.10.12.13.29.31.Dec.2011 kei
 * 13.Jan.2012
 * 21.Aug.2012
 */

#include <lwd/Environment.h>

#include <lwd/throw.h>
#include <stdio.h>
#include <stdlib.h>

/****************************************************************
 private
****************************************************************/

static lwd_Environment lwd_environment = (void*)0;

static void push(lwd_Environment environment) {
        environment->chain = lwd_environment;
        environment->throwable = NULL;
        lwd_environment = environment;
}

static void pop(lwd_Environment environment) {
        lwd_environment = environment->chain;
}

static lwd_boolean isEmpty() {
        return (lwd_environment == NULL);
}

static void setThrowable(lwd_Environment environment) {
        lwd_environment->throwable = environment->throwable;
}

static struct lwd_EnvironmentSPI singleThreadSPI = {
        push,
        pop,
        isEmpty,
        setThrowable
};

static lwd_EnvironmentSPI environmentSPI = &singleThreadSPI;

/****************************************************************
 public
****************************************************************/

const lwd_boolean lwd_Environment_catchClause = lwd_false;

void lwd_Environment_push(lwd_Environment environment) {
        environmentSPI->push(environment);
}

void lwd_Environment_pop(lwd_Environment environment) {
        environmentSPI->pop(environment);
}

lwd_boolean lwd_Environment_isEmpty() {
        return environmentSPI->isEmpty();
}

void lwd_Environment_setThrowable(lwd_Environment environment) {
        environmentSPI->setThrowable(environment);
}

void lwd_Environment_throw(lwd_Throwable throwable) {
        lwd_environment->throwable = throwable;
        longjmp(lwd_environment->buffer, 1);
}

void lwd_Environment_finally() {
}

void lwd_Environment_abort(lwd_Throwable throwable) {
        printf("uncaught exception '%s' occured.\n",
                lwd_Throwable_getType(throwable));
        exit(1);
}

void lwd_Environment_setSPI(lwd_EnvironmentSPI spi) {
        environmentSPI = (spi != NULL)
                ? spi
                : &singleThreadSPI;
}

デフォルトの SPIはシングルスレッド用である。
マルチスレッドに対応するためには、使用するスレッド機構(例えば pthread)用の SPIを記述し、差し替える必要がある。
abort関数の中で、printfを使用しているが、これは手抜き。fprintfで stderrに出力すべきだ。

テスト (test)

簡単なテストケース ExceptionExampleTest.cをつぎに示す。
/* 15.Aug.2012 kei */

#define import_lwd_unit_CUnit

#include <lwd/lwd.h>
#include <lwd/throw.h>
#include <lwd/Throwable.h>
#include <lwd/unit/CUnit.h>
#include <stdio.h>

lwd_Environment lwd_environment = NULL;

typedef struct TestException* TestException;

struct TestException {
        struct lwd_Throwable throwable;
        char* message;
};

TestException new_TestException(char* message) {
        TestException this =
                (TestException)malloc(sizeof(struct TestException));
        if (this != NULL) {
                lwd_Throwable_Throwable((lwd_Throwable)this, "TestException");
                this->message = message;
        }
        return this;
}

void testException() {
        try {
                throw(new_TestException("test"));
        } catch (TestException, e) {
                printf("TestException caught, %s\n", e->message);
        } finally {
                printf("finally\n");
        }
}

int main(int argc, char** argv) {
 return CUnit_test(argc, argv);
}

実行結果は、つぎの通り。
$ ./ExceptionExampleTest
[CUnit] ./ExceptionExampleTest: testException
TestException caught, test
finally
[SUCCEED] testException in 2(ms)
successes:    1 (100.0%)
failures:     0 (0.0%)
errors:       0 (0.0%)
total:        1 in 2(ms)
$ 

ごっそり、秘密をあげるわ。

2012年8月14日火曜日

継承 その2 (Inheritance 2)

継承 (Inheritance)や オーバライド (override)ではサブクラスのインスタンス、関数テーブル構造体の中でスーパクラスのメンバを再定義していた。 今回はこれのうちインスタンスの構造体について、スーパクラスのメンバを記述しないで済む方法を考察する。 なお、関数テーブルもいろいろ試みてみたが、まだ良い方法が見つかっていないため、当面は現状の方式でいこうと思う。

なお、サブクラスの例としては IntegerVariableクラスは不適切である(サブクラスがスーパクラスの privateメンバを直接アクセスしていた)ため、LoggableIntegerクラスを用いる。

バイト列による置き換え (substitution by the byte array)

問題としているのはつぎの箇所である。 (LoggableInteger.cの一部)
struct LoggableInteger {
 interface_LoggableInteger interface_methods;

 /* Integer class */
 int value;

 /* LoggaableInteger class */
 int counter;
};

これの int valueの部分をサイズを保ったまま、抽象化してしまうために、つぎのようにしてみる。
struct LoggableInteger {
        interface_LoggableInteger interface_methods;
        char padding[sizeof_Integer];

        int counter;
};

sizeof_Integerは Integer.hで defineされている。これを char配列のサイズとすることでスーパクラスのインスタンスサイズを確保することができる。


サイズの定数 (constant number of the size)

問題は、sizeof_Integerをどのように求めるかである。
Integerクラスのインスタンス構造体のサイズを求めるには、
sizeof(struct Integer) 
とすれば良いのだが、隠蔽するために struct Integer構造体は Integer.hではなく、Integer.cに記述している。
したがって、LoggableIntegerクラスが Integer.hをインクルードしても、sizeofでサイズを求めることはできない。 そこで苦肉の策として、定数を直接ヘッダファイル Integer.hに記述している。
#define sizeof_Integer  4

そうすると、Integerクラスのインスタンス変数を変更すると、この値を忘れずに修正しなければならない。しかも、処理系によってメンバが整列されることがあるため、計算は単純ではない。
そこで、つきのような引数付きマクロを使って実行時に自己チェックするようにしてみる。
#define interface_sizeof(CLASS) \
        if (sizeof_##CLASS != sizeof(struct CLASS) - sizeof(void*)) { \
                printf("sizeof struct %s expected %d but %d\n", \
                        #CLASS, \
                        sizeof(struct CLASS) - sizeof(void*), \
                        sizeof_##CLASS); \
                abort(); \
        }


このマクロをつぎのように、クラスの初期化関数の中で呼び出す。
void static_Integer(interface_Integer interface_methods) {
        /* ToDo: lock interface_methods */
        if (interface_methods->finalize == NULL) {
                interface_sizeof(Integer);

                interface_methods->finalize = finalize;
                interface_methods->getValue = getValue;
        }
}


sizeof_Integerの値は最初は適当な値にしておけばよい。たとえば次のように 0としておく。
#define sizeof_Integer  0


これで以前に示したテストケースを実行すると、
$ ./IntegerTest
[CUnit] ./IntegerTest: testGetValue
sizeof struct Integer expected 4 but 0
signal: Abort(6)
[ABORT] testGetValue
[CUnit] ./IntegerTest: testNewAndFinalize
sizeof struct Integer expected 4 but 0
signal: Abort(6)
[ABORT] testNewAndFinalize
[ERROR] testGetValue in 803(ms)
[ERROR] testNewAndFinalize in 811(ms)
successes:    0 (0.0%)
failures:     0 (0.0%)
errors:       2 (100.0%)
total:        2 in 1614(ms)
$ 

このように、struct Integerのサイズは 4バイトであることが判明するため、defineの 0を 4に修正することができる。
一旦失敗して表示された値を用いる 2パス方式は、なんとも間抜けな気もするが、unit testを推進するという口実にしておこう。

なお、マクロはこのクラス以外でも使用したいために、適当なヘッダファイルに記述することにする。
また、このマクロは printf関数を使用しているため、どこかで stdio.hをインクルードする必要がある。
クラスによっては stdio.hをインクルードしたくない場合もあるため、この点は今後の課題とする。

あの少女は まるで恋だね。

2012年8月13日月曜日

オーバライド (override)

前回の 継承 (Inheritance)ができたので、これを応用すればスーパクラスで定義されている関数をサブクラス側で上書きする、オーバライドができる。


Integerクラスのサブクラス LoggableIntegerクラスには、スーパクラスの getValue関数をオーバライドする。この関数は Integerのそれど同様 int型の値を返すが、この関数を呼び出した回数を変数 counterに記録し、getAccessCount関数で読み出せるようにする。








ヘッダファイル (header file)

LoggableInteger.hはつぎの通り。
/* 13.Aug.2012 kei */

#ifndef _Included_LoggableInteger
#define _Included_LoggableInteger

typedef struct LoggableInteger* LoggableInteger;

typedef struct interface_LoggableInteger {

        void (*finalize)(LoggableInteger this);
        int (*getValue)(LoggableInteger this);

        int (*getAccessCount)(LoggableInteger this);
}* interface_LoggableInteger; 

extern void static_LoggableInteger(interface_LoggableInteger interface_methods);

extern LoggableInteger new_LoggableInteger(int value);

extern void LoggableInteger_init(LoggableInteger this, int value);

extern void LoggableInteger_finalize(LoggableInteger this);

extern int LoggableInteger_getValue(LoggableInteger this);

extern int LoggableInteger_getAccessCount(LoggableInteger this);

#endif /* _Included_LoggableInteger */

変数や関数が違う点以外は IntegerValueクラスのヘッダと同様。

ソースファイル (source file)

LoggableInteger.cはつぎの通り。
/* 13.Aug.2012 kei */

#include <LoggableInteger.h>
#include <Integer.h>

/****************************************************************
 private
****************************************************************/

#include <malloc.h>

struct LoggableInteger {
        interface_LoggableInteger interface_methods;

        /* Integer class */
        int value;

        /* LoggaableInteger class */
        int counter;
};

static struct interface_LoggableInteger interface_methods;
static struct interface_Integer super;

static int getValue(LoggableInteger this) {
        ++this->counter;
        return Integer_getValue((Integer)this);
}

static int getAccessCount(LoggableInteger this) {
        return this->counter;
}

/****************************************************************
 public
****************************************************************/

void static_LoggableInteger(interface_LoggableInteger interface_methods) {
        /* ToDo: lock interface_methods */
        if (interface_methods->finalize == NULL) {
                static_Integer(&super);
                static_Integer((interface_Integer)interface_methods);

                interface_methods->getValue = getValue;
                interface_methods->getAccessCount = getAccessCount;
        }
}

LoggableInteger new_LoggableInteger(int value) {
        static_LoggableInteger(&interface_methods);

        LoggableInteger this =
                (LoggableInteger)malloc(sizeof(struct LoggableInteger));

        if (this != NULL)
                LoggableInteger_init(this, value);

        return this;
}

void LoggableInteger_init(LoggableInteger this, int value) {
        Integer_init((Integer)this, value);
        this->interface_methods = &interface_methods;
}

void LoggableInteger_finalize(LoggableInteger this) {
        this->interface_methods->finalize(this);
}

int LoggableInteger_getValue(LoggableInteger this) {
        return this->interface_methods->getValue(this);
}

int LoggableInteger_getAccessCount(LoggableInteger this) {
        return this->interface_methods->getAccessCount(this);
}

これも IntegerVariableクラスとほぼ同じだが、getValue関数をオーバライドするため、LoggableIntegerクラスで改めて定義し、static_LoggableIntegerで getValue関数を設定している。
static_Integer関数を呼び出した時点で、interface_methodsには一旦スーパクラスの getValue関数が設定される。その後でサブクラスの関数を上書きしている。 概念だけでなく実際に上書きしているので、如何にも "override"の感じがする。

関数テーブルは interface_methodsのほかに Integerクラス用の superも定義している。
これは、Integerクラスの getValueを呼び出すために必要となる。

getValue関数の中では、 super.getValue((Integer)this); を呼び出すことにより、スーパクラスの取得関数を利用している。

動作確認 (unit test)

テストケース LoggableIntegerTest.cはつぎの通り。
/* 14.Aug.2012 kei */

#define import_lwd_unit_Assert
#define import_lwd_unit_CUnit

#include <lwd/unit/Assert.h>
#include <lwd/unit/CUnit.h>
#include <LoggableInteger.h>

/****************************************************************
 test cases
****************************************************************/

static LoggableInteger instance;

void setUp() {
        instance = new_LoggableInteger(813);
}

void tearDown() {
        LoggableInteger_finalize(instance);
}

void testNewAndFinalize() {
        /* setUp, tearDown */
}

void testGetValue() {
        int value = LoggableInteger_getValue(instance);
        Assert_equalsInt("A value must be 813.", 813, value);
}

void testGetAccessCount() {
        Assert_equalsInt("must be zero.",
                0, LoggableInteger_getAccessCount(instance));

        LoggableInteger_getValue(instance);

        Assert_equalsInt("must be 1.",
                1, LoggableInteger_getAccessCount(instance));
}

/****************************************************************
 main
****************************************************************/

int main(int argc, char** argv) {
        return CUnit_test(argc, argv);
}

テストのビルドと実行結果も残しておく。
$ make
cc -g -finstrument-functions -I. -I /export/home/kei/c/core/include -I /export/home/
kei/c/experimental/include -I /export/home/kei/c/io/include -I /export/home/kei/c/lang
/include -I /export/home/kei/c/message/include -I /export/home/kei/c/unit/include -I
 /export/home/kei/c/util/include -I /export/home/kei/c/x11/include -I ../../char/
include -I ../../class1/include  -c -o LoggableIntegerTest.o LoggableIntegerTest.c
$ ./LoggableIntegerTest
[CUnit] ./LoggableIntegerTest: testGetAccessCount
[CUnit] ./LoggableIntegerTest: testGetValue
[CUnit] ./LoggableIntegerTest: testNewAndFinalize
[SUCCEED] testGetAccessCount in 1(ms)
[SUCCEED] testGetValue in 2(ms)
[SUCCEED] testNewAndFinalize in 2(ms)
successes:    3 (100.0%)
failures:     0 (0.0%)
errors:       0 (0.0%)
total:        3 in 5(ms)
$ 

竜巻情報が出された。豹が降る恐れがあるとか言っている。