* KHPCのページ -(by [[K]], 2016.02.18) ** KHPCとは? -KHPCは persistent-C の実装のひとつ。 -基本方針としては、手抜き。 -persistent-Cがどんなものなのかある程度実感できればよく、処理系としての優秀さは追求しない。 -さまざまな仕様を試すためにも、厳密さにこだわって試行錯誤を遅らせるのは得策ではない。 ** ver.0.00 -最初のバージョン -ソーストランスレータ部を持たない、ランタイムライブラリのみ。 -とにかくできるだけ手抜きをする。次のバージョンでまともにすればいい。手抜きは美徳である。 --persistent_init() : 内部テーブルの初期化 --persistent_def(void *a, size_t size, int grpId, int flags, const char *id) : テーブルに登録 ~ -永続変数は、いずれもなんらかの「グループ」に所属する必要がある。ここで、グループというのはセーブファイルと同義語だと思ってよい。つまり変数ごとに、どのファイルに保存するかを選べる。現状では、1つのプログラム内で設定できるグループは最大で4つ程度を想定している。grpId=0はデフォルトグループであり、特に理由がなければ、このグループに所属させて使うのが好ましい。 --セーブファイルを複数使えるようにした背景としては、セーブファイル間の変数コピーを容易にしたかったということがある。 --セーブファイルが別ならば、永続変数idが共通でも、別のものとして扱われる。 -永続変数のidとは、変数名、変数の型、変数のサイズなどを反映した文字列である。これが一致しなければ、ロードされることはない。 --当面の開発計画におけるデフォルトでは変数名以外の情報が反映されないが、persistent_def関数を直接使えば、より複雑な設定が可能になる。 ---というかver.0.0ではpersistent_defしかないのだけど。 -persistent_loadを実行すると、それ以降はpersistent_defなどの追加設定はできない。 -saveに先だって、loadを一度は実行しておかなければならない。 --loadし忘れを防止するため。loadしないでsaveしたらいつも上書き保存になってしまって、永続変数を使う意味がない。 --セーブファイルがない状態でloadしても何も起きないだけなので、問題ない。 -セーブされている変数と、メモリ上の変数とでサイズが違う時の挙動は以下の通り。 --メモリ上の方が大きい場合: ロード時にはファイルに存在している部分までが読み込まれる。セーブ時には新しい大きなサイズで書き込まれる。 --ファイル上の方が大きい場合: まずメモリに読み込めるところまで読み込む。次にファイルの残りの部分を読み込んで、それらが全部'\0'であることを確認する。確認した結果'\0'以外があればエラー終了となり、プログラムは停止させられる。 ---つまり末尾ゼロは削られるが、それ以外の場合は削らない。 ---極力データを失わないようにするため。 ---将来開発予定のセーブデータ管理ツールを使えば、末尾が'\0'ではなくてもサイズを切り詰められる。 -flagsの仕様: --flagsのどこかのbitが1だと、型情報フィールドが存在する。どこのbitにするかはまだ決めてない。 --flagsのどこかのbitが1だと、リマークフィールドが存在する。どこのbitにするかはまだ決めてない。 -型情報の書き方(案): --基本型はu#,s#,f#,#の4通りしかない。#は10進数の数字。s32なら符号付きの32bit整数。アルファベットなしで数字で始まった場合、用途は不明だがとにかくそのビット数ぶんだけ情報がある(もしくはパディング)。 --(s32*3:a,u8*4:c)*16は以下の意味になる。 struct { int a[3]; unsigned char c[4]; } ???[16]; ---コロンを打てばそのフィールドに名前をつけられる(名前はあってもなくてもいい)。カンマで区切れば連結する。括弧でくくることもできる。掛け算記号で配列を表現できる。|記号を使えばunionも表現できる。 --型情報は今のところはリマーク扱いなので、KHPCはこれを感知しない。 ** ver.0.01 (旧ver.0.00) -ソース・トランスレータ部と、ランタイムライブラリ部に分かれる。 -とにかくできるだけ手抜きをする。次のバージョンでまともにすればいい。手抜きは美徳である。 --とりあえず保存するファイルのファイル名も固定にしてしまおう(本来の仕様には反しているけど)。 -ソーストランスレータ部: --極端なことを言えば、ソースからpersistentの語を消せば、ソースのトランスレートはほぼ終了である(笑)。 --あとは下記のpersistent_init()に必要な情報を生成する。 --今回は手抜きのため、行頭の「persistent」にしか反応しないし、関数内のstatic変数のpersistentには対応しない(エラーになるか、誤作動する)。 -ランタイムライブラリ部: --persistent_load()およびpersistent_save()はまず最初にpersistent_init()を呼び出す。 --persistent_init()はランタイム部が既に初期化済みなら何もせずにreturnする。 --初期化に当たっては、ソーストランスレータ部が生成した永続変数情報を内部テーブルに展開する。 --とりあえず面倒なので、256個程度が登録できればよい。 -save動作: --まずイメージファイルを全部読みこんでしまう。適当に上限64MBとかでいいと思う。 --ファイルが存在しなければ、空っぽのイメージを構築する。 --次に永続変数テーブルを参照して、該当する物があれば上書きする。なければ追加する。 --サイズが違ったら、旧サイズのものはイメージ上から削除する(上書き動作なので)。 --そしてイメージファイルを全部出力する。 -load動作: --ファイルが存在しなければ何もしなくてよい。 --まずイメージファイルを全部読みこんでしまう。適当に上限64MBとかでいいと思う。 --ファイルの中身を参照して、対応する永続変数があればコピーする。なければそれは何もしない。 * こめんと欄 #comment