ここでは classの実現方法はひとまず置いといて、softies projectに於ける structの使用方法について考察してみる。
Kernighan und Ritchieより
プログラミング言語 Cの 6.9章では、構造体を typedefする例としてつぎのものが示されている。typedef struct tnode { /* the basic node */
char *word; /* points to the text */
int count; /* number of occurrences */
struct tnode *left /* left child */
struct tnode *right; /* right child */
} TREENODE, *TREEPTR;
このように、typdefと同時に structを定義する場合について考える。もう少し簡単例を示す。
typedef struct Type {
Type* child;
} Type;
typedefと struct定義を 1つの分で記述するとすっきりするのだが、自己参照(あるいは循環参照)する場面では、定義が完結していないため内部で typedefした結果を使用することができない。typedefと structを 2つの文で定義することで解決される。
typedef struct Type Type;
struct Type {
Type* child;
};
さすがに最近はこのようなコードを示しても不思議がられる事は無いが、90年代にはしばしば「ビルドが通るか?」と疑問が投げかけられ、そのたびに説明が必要だった。
疑問の1つ目は、structの名前と typedefする名前がどちらも Typeで良いのか?というものであった。タグをつけるときには Type_tのようにするが、ここでもそうしなければならないという主張であった。 言語 Cでは両者が一緒の名前でも問題はない。 struct Typeと Typeを使い分けなければならない場面は明確で紛らわしくはならないし、別々の名前を考えるほうが手間が生じるため、両者は一緒にしておいたほうが楽である。
2つ目は、struct定義よりも先に typedefできるのか?というものである。 K&Rの 6.9では typedefが「宣言」と書かれていることから判るように、定義は不要である。
そして3つ目は、循環参照する設計は悪い!というものである。 しかし、オブジェクトを分析、設計すると循環するのが自然なものに遭遇する。 GoFが浸透してからは Composite Patternと言えば納得してもらえるようになった。
ポインタも含めてしまおう
オブジェクトを mallocにより heapから割り当てることが多いため、いちいち Type*のようにポインタであることを明示しなくとも、Typeで済ませたい。typdef struct Type* Type; /* pointer included */
struct Typeと書かなければならないのはそれを定義するときと sizeof演算子で構造体の(ポインタではなく本体の)サイズを求めるときくらいだから、このように、* も typedefの中に含めてしまえばすっきりする。
C++との互換
softies projectは言語 Cのみを対象としていて、C++との相互利用は原則として考慮されていない。しかし、softiesのテストフレームワーク CUnit packageは CppUnitよりも便利かも(手前味噌)と感じたので、自動車業界での組み込み系 C++プロジェクト内で実際に使ってみた。すると当然ながら Cと C++の違いによる問題が幾つか表面化したのだが、typedefでは上述の typedef struct Type* Type;のように構造体名と typedefの名前を一緒にできないことが判明した。 C++からもアクセスする用途では、ちょっと癪に障るが、typedef struct Type_t* Type; /* when the use is required from C++ */
のように両者の名前を変えるのがよい。幸い、CUnit packageではこのようにしなくとも回避することができたのと、softies projectの CUnit以外の成果を C++から使用する意義がない(オブジェクト指向言語であるC++はその真似事をする必要がないのと、専用のライブラリが充実している)ため、暫くはこの回避策は必要なさそうだ。
このままで挿れたら♥ って思う瞬間まで