sogen
はユーザーが定義した構造体の操作コードを生成するコード・
ジェネレータです。構造体を格納する可変長コンテナを用意し、追加・削除・
変更・ソート・検索等の操作をユーザーに提供するものです。ユーザーは
構造体の定義だけすればよく、後はsogen
に任せればよいのです。
C++ではSTLが使えて不便な思いはしませんが、Cだけの環境では同じような コードを何度も書いて、且つバグと戦わなければならないことを考えると このようなコード・ジェネレータが必要と思われました。しかも、 ポータブルな。
ユーザーはaddress.h
を作成します。
ソース生成の都合上、ターゲットとなる構造体はtypedef
でそれ用の型を定義しておいてください。
struct _ADDRESS
のままだとsogen
は使用できません。ご注意ください。
typedef struct _ZIP { int i_key; char zip[7]; } ZIP; typedef struct _PERSON { int i_key; char name[16]; } PERSON; typedef struct _ADDRESS { int i_key; char name[256]; char *p_symbol; ZIP zip_code; PERSON *person; } ADDRESS;
sogen
は以下のようにして使用します。sogen
はコードを生成する
際にaddress.h
は読みません。単純にコマンドライン引数から得た情報から
単調にソースを作るだけなのです。
$ sogen -i address.h -n ADDRESS -k i_key
これにより、2つのファイルaddress_mgr.c
とaddress_mgr.h
が
生成されます。ユーザーはaddress_mgr.h
をインクルードすれば、
構造体操作関数が使えます。
#include <stdio.h> #include "address_mgr.h" ... int main(int argc, char **argv) { size_t i; ADDRESS adr; adr.i_key = 0; strcpy( adr.name, "masu" ); ... address_push_back( &adr ); /* コンテナに追加 */ ... address_push_back( &adr ); /* コンテナに追加 */ ... address_push_back( &adr ); /* コンテナに追加 */ ... address_sort(); /* sort */ /* 全要素の name を表示 */ for ( i = 0; i < address_size(); i++ ) { printf( "name: %s\n", address_at( i )->name ); } ... address_clear(); /* メモリ解放 */ return 0; }
sogen
により生成された操作関数を以下に示します。
NAMEは構造体の名前1です。nameはNAMEを全て小文字にした文字列です。
int name_push_back( const NAME *p )
memcopy
のイメージでコピーします。
内部でメモリ確保に失敗した場合は0
以外を返します。正常な場合は
0
を返します。
void name_pop_back( void )
void name_touch_pop_back( void ( *fn )( NAME * ) )
fn
を実行
します。fn
は通常、要素のクリーンアップ関数として使われます。
void name_clear( void )
void address_touch_clear( void ( *fn )( NAME * ) );
fn
を
実行します。fn
は通常、要素のクリーンアップ関数として使われます。
size_t address_size( void )
const NAME * address_at( size_t i )
int name_find( NAME **pp )
name_findfirst( NULL, NAME ** )
と同じです。
コンテナの最初の要素へのポインタを獲得します。要素があれば0
以外を、
要素がなければ0
を返します。要素を総ナメしたしときの最初に呼ぶ
関数です。
int name_findfirst( int ( *fn_flt )( NAME * ), NAME **pp )
fn_flt
で真を返す要素を検索します。最初に条件を満足した要素への
ポインタを獲得します。条件を満足する要素があれば0
以外を、条件を
満足する要素がひとつもないときは0
を返します。
次に条件を満足する要素を探したいときは、関数name_findnext
を呼びます。
前回の条件を満たした位置は静的に保持しています。
int name_findnext( int ( *fn_flt )( NAME * ), NAME **pp )
name_findfirst
、またはname_findnext
自身を呼んだ後
に呼びます。前回のヒットした位置より
後方を、条件関数fn_flt
を満たす要素を検索します。
条件を満足した要素へのポインタを獲得します。
条件を満足する要素があれば0
以外を、条件を
満足する要素がひとつもないときは0
を返します。
条件を満たした位置は静的に保持しています。
NAME * name_bsearch( NAME *p )
p
で指定した要素と一致する要素がコンテナに存在するかサーチします(binary search)。
一致する要素がある場合にはその要素へのポインタを返します。なければNULLを
返します。
sogen
実行時に主キー(primary key)を指定(-k,-K
)した
場合にのみ使用できます。
比較関数を別途用意する場合は、以下のname_bsearch_by
関数を使用します。
NAME * name_bsearch_by( NAME *p, int ( *fn_cmp )( const void * ,const void * ) )
fn_cmp
を指定して、p
で指定した要素と一致する要素がコンテナに存在するかサーチします(binary search)。
一致する要素がある場合にはその要素へのポインタを返します。なければNULLを
返します。
比較関数の引数の型がconst void *
なのは、標準関数bsearch
に渡す
比較関数の型に依存しているからです。
void name_sort( void )
sogen
実行時に主キー(primary key)を指定(-k,-K
)した
場合にのみ使用できます。主キーが値、文字列の場合とも昇順です。比較関数を
別途用意する場合は、以下のname_sort_by
関数を使用します。
void name_sort_by( int ( *fn_cmp )( const void * ,const void * ) )
fn_cmp
を指定して、コンテナ内をソートします(quick sort)。
比較関数の引数の型がconst void *
なのは、標準関数qsort
に渡す
比較関数の型に依存しているからです。
void name_transform( void ( *fn )( NAME * ) )
fn
を実行します。
要素に対する変更・更新を一括して行う場合に使用します。
void name_transform_if( int ( *fn_flt )( NAME * ), void ( *fn )( NAME * ) )
fn_flt
を実行して真を返す要素にのみ、関数fn
を実行します。
選別された要素に対する変更・更新を一括して行う場合に使用します。
sogen
の起動オプションです。
-i <include_file>
-n <NAME>
-k <primary_key_val>
-K
オプションと同時に使用できません。
-K <primary_key_string>
-k
オプションと同時に使用できません。
-s <body_suffix>
sogen
が生成するファイル名のサフィックスを指定します。
デフォルトでは_mgr
となっています。例えば-n FOO -s _aaa
と指定した
場合、生成させるファイルは、foo_aaa.c
とfoo_aaa.h
になります。
-h
-V
sogen
のバージョン情報を表示します。
コンテナは可変長配列として実装しています。 初期サイズは128です。このサイズをオーバーするときは新たに、その2倍の 容量を確保しなおします。つまり、128を超えるときは256です。256を超えるときは 512です。少々メモリが無駄になりますが、オーダー的には収まっていて手軽な 実装方法です。
sogen
のソースコードの一部は、opgen
により生成されています。
opgen
はコマンドライン・パーサーを生成するコード・ジェネレータです。
sogen
は実体をユーザーから隠蔽した可変長コンテンナを提供しますが、
生憎その数は唯一です。ですから、コンテンナの配列、構造体メンバ中にコンテンナ
を配する等、よく出会いそうな問題には対処できません。
そこでこれらの問題に対処すべくsogen
を拡張する予定です。
バグリポートは massun.masumoto@nifty.ne.jpへお願いいたします。 また、現在のところ公開は作者のWebサイト http://member.nifty.ne.jp/~masumoto/ で行います。