メモ。明日から予約開始。買うかどうか微妙なところ。
原子力発電の年間維持費が三億円…ではなくて、広報用ウェブページの年間維持費が三億円だそうです。ぶっちゃけありえない♪。何をどう見積もればこんな金額になるのか、教えて!!知○留先生。
7-4の例の難所がクリアできません。壁面走行、滑空、三角跳びを繰り返すということはわかっているのですが、壁に掴まってくれなくてずるずる落ちていってしまいます。ちゃんと掴まれよ、ga○kt。
TJSエンジン引っこ抜きメモ。VCL依存だけは避けたいところですが、どうなることやら。
サンプルコードではwindows.hとSTL stringをインクルードすること。そのままだとコンパイルエラーになった。
#include <windows.h> #include <stdio.h> #include <string> #include "tjs.h" #include "tjsError.h"
TJSのエンジン部をDLLにしました。コマンドライン版TJSを作るだけなら、わざわざDLLにする必要はないのですが、ほかの計画で流用する予定なのでこうしておきます。
DLLからエクスポートされた関数の呼び出し方は、通常はGetProcAddress()関数を使って、DLLインスタンスハンドルと関数名から関数ポインタを取得し、それを実行します。しかし、利用者側が毎回こんなことをするのは面倒なので、DLLを作成する時点でラッパー関数を定義するコードも作成します。もちろん自動生成です。このコードを、DLL利用者がコンパイルすることで、エクスポートされた関数を簡単に利用できるようにします。
inline tTJS * TJSEGetScriptEngine() { if (!TJSEImportFuncPtr_TJSEGetScriptEngine) { static char funcname[] = "TJSEGetScriptEngine"; TJSEImportFuncPtr_TJSEGetScriptEngine = GetProcAddress(TJSEDLLInstanceHandle, funcname); } typedef tTJS * (WINAPI * __functype)(); return ((__functype)(TJSEImportFuncPtr_TJSEGetScriptEngine))(); }
static変数のせいでinline展開できないのはご愛敬ということで。そのうち対処方法を考えます。ちなみに、このコードはtp_stub.hのコードを恥も外聞もなく真似たものです。
ネイティブ関数を追加するための機構とマクロを追加しました。これを使って、 global.print(String) 関数を作成しました。TJSから標準出力にナロー文字列を出力する関数です。とりあえず、こんなコードが動きました。
if (TJSELoadLibrary() == NULL) { printf("ロードエラー\n"); return -1; } TJSEExecScript( L"function test(x, y) { return x*y; } \n" L"var res = '結果 = ' + test(4, 5); \n" L"print(res + '\\n'); \n", NULL, NULL, TJS_W("test code"));
DLLロード直後にスクリプトを実行できるあたりが、ちょっぴり素敵です。
ほか、早いうちに実装しておきたいのがファイル入出力関連です。TJSエンジンには、標準ではファイル入出力機能が実装されていないっぽい(インターフェースだけが提供されている)ので、Array.load()等が使えません。吉里吉里ではたしかVCLを使って実装していたような気がしますが、今回はVCL非依存を前提にしているので、いつものように(?)パクって済ませるわけにはいかなそうです。
I/O関連の実装を始めました。吉里吉里ではVCLを使っていたと思ったのですが、TVP2.24RC3 *1 のソースを追いかけてみたら、VCLは使われていませんでした。気のせいだったのか、記憶違いか…確か以前TStreamクラスとか使っていたような…まあいいか。
で、追っかけですが、吉里吉里のストレージ周りは複雑怪奇でよくわかりませんでした。下手すると、レイヤ周りよりも複雑かも。とりあえずファイルストリームとファイルストレージの部分だけを取り出して、うまい具合につなぎ合わせて、モジュール化。吉里吉里に深く依存した部分を所々コメントアウトしているため、動作はちょっと怪しいかもしれません。特にエラー処理。まあ追々直していくとしましょう。
ていうか、結局パクりかよ。
TJSCreateTextStreamForReadに関数の実体(へのポインタ)をコピーするところまで漕ぎ着け、Array.load()が動作するのを確認して終了。書き込み周りは、そのうち気が向いたらパクる実装するとしましょう。
簡易版スタブモジュールを作成しました。通常版スタブをリンクすれば、TJS関係の機能(tTJSStringとかeTJSとかいろいろ)を使えますが、バイナリのサイズはかなり大きくなってしまいます。簡易版では、TJS関連を一切使えませんが、TJSライブラリをリンクしないので、バイナリのサイズは小さくなります。この簡易版を使ってコマンドライン版を作成したところ、実行ファイルが64KB、DLL(TJSエンジン)が1MBとなりました。
ところで、当初の計画にはあった、プラットフォーム非依存の実行環境ですが、Windows APIに依存している部分が多々あったり、MakefileがBorland Makeに特化してたり、その他アレ(何)だったりで、現状では無理そうです。bash on Linux上でtjs HelloWorld.tjsとか叩けると楽しそうなんですけどね。
あと、コメントをちゃんと書いて、Doxygenで出力させてみました。
例の登れなかった場所ですが、ふとやり方が閃いたので、登ることができました。要するに、
ということですね。
そのまま勢いで、エンディングまでいきました。
メッセージ周りのインターフェースを作りました。TJS本体で定義されているメッセージを利用できる形にしました。
おや、かぶってしまいましたか。
わたなべごうさんのは、吉里吉里プラグインにsqlite3本体を組み込んだ形になっています。また、2.25系の(俺的には)目玉機能である「プラグインでネイティブクラス」を利用しています。
私のは吉里吉里プラグインとsqlite3本体が別になっています。sqlite3本体は吉里吉里とは無関係なDLLですので、吉里吉里以外からも利用できますが、使う人いるんかいな。また、2.24系なので「プラグインで無理矢理クラス」です。インチキくさい。
ああ、なるほど、iTJSConsoleOutputを使えばいいのですね。
class TJSEStandardOutput : public iTJSConsoleOutput { void ExceptionPrint(const tjs_char *msg) { printf("%ls\n", msg); } void Print(const tjs_char *msg) { printf("%ls\n", msg); } };
このクラスのインスタンスを、tTJS::SetConsoleOutput()でエンジンに食わせてやると、エラー発生時に標準出力にこんなものを吐いてくれます。
==== An exception occured at A simple script invoked from TJSEExecSimpleScriptSTDC()(6)[(top level script) global], VM ip = 54 ==== -- Disassembled VM code -- …(略) -- Register dump -- …(略) ----------------------------------------------------------------------------------------------------------------------------------- メンバ "foo" が見つかりません at A simple script invoked from TJSEExecSimpleScriptSTDC()(6)[(top level script) global]
つまり、コールバックインスタンスとして機能させるわけです。違う実装を食わせてやれば、違う出力が得られる、と。いい作りしてますね。素晴らしい。これならエラー処理しなくていいじゃないか、なんていう錯覚に陥りそうになりました。いや、実際にはやることはいろいろありますよ。