人の話を聞かない人の V8 エンジン。 V8 を C++ に組み込んで遊ぼう
C++ Advent Calendar です。
google の V8 engine を 組み込んで簡易スクリプトにしてしまおうという話です。
C++の言語仕様や boost の話は超絶詳しい人たちがされているので、誰もやらなそうな ぬるいいい加減な話をします。
#間違ってたら突っ込みよろしく!!
なぜ V8? Lua ぢゃだめなんですか?
C++ から呼び出しやすい 組み込み言語として、 Luaがあります。
しかし、 Luaはコメントが -- から始まったり、 〜end と、
C / C++ に組み込みやすいスクリプト言語なのになぜか pascal チックな文法?という不思議なところがあります。
やはり、 C++ ユーザとしてはコメントは // で書きたいし、ブロックは { } でやりたいですよね (y/Yes)
そんなところに、 google の V8 engine があります。
C++に組み込めます。 速い(らしい)です。javascript です。 javascript のコメントは // です。ブロックは { } です。
これは使ってみるしかありません。
ここでは、 V8 engine + VS2010 という不思議な組み合わせで、 V8 engine を使う方法を簡単に説明したいと思います。
V8 build は省略。
ソースを落として、ビルドする、、、、は、前に「厨房でもできる windows VS2010 で V8 engine をビルド 」で書きましたので、そちらを参考にしてください。
#Linux などの Unix 系のユーザーだったら、もっと楽に構築できると思いますがww
V8 で hello world!
ここでは、初心者向けに少しづつ説明していきます。(自分が試行錯誤した結果の記録です)。わかっている人は読み飛ばしてください。
さて、
V8 Engine をビルドできたとします。
これを利用して hello world をやってみます。
新規に VS2010 でプロジェクトを作ります。
V8 engine の公式サイトに乗っているドキュメントからコピペしてきたコードを適当に貼り付けてビルドしてみます。
http://code.google.com/intl/ja/apis/v8/get_started.html
#include <v8.h> using namespace v8; int main(int argc, char* argv[]) { // Create a stack-allocated handle scope. HandleScope handle_scope; // Create a new context. Persistent<Context> context = Context::New(); // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // Create a string containing the JavaScript source code. Handle<String> source = String::New("'Hello' + ', World!'"); // Compile the source code. Handle<Script> script = Script::Compile(source); // Run the script to get the result. Handle<Value> result = script->Run(); // Dispose the persistent context. context.Dispose(); // Convert the result to an ASCII string and print it. String::AsciiValue ascii(result); printf("%s\n", *ascii); return 0; }
OK。
では、ビルドしてみます。
1>ClCompile: 1> stdafx.cpp 1> v8_test.cpp 1>c:\users\rti\documents\visual studio 2010\projects\v8_test\v8_test\v8_test.cpp(5): fatal error C1083: include ファイルを開けません。'v8.h': No such file or directory 1> 1>ビルドに失敗しました。
はい、エラーでました >_<。
v8.h がないそうです。
あー、そうだよね。include のパスを通すのをすっかり忘れていました。
左側のプロジェクトエクスプローラーのところで右クリックし、プロパティを開きます。
プロジェクトのプロパティページを開き、「追加のインクルードディレクトリ」 の項目に、V8 engine の src ディレクトリを指定します。
私は、デスクトップの上に作った v8 というフォルダでビルドしたので、C:\Users\rti\Desktop\V8\src と、なりました。
さて、追加のインクルードディレクトリもできました。
よし。もう一度ビルドしてみましょう。
1> stdafx.cpp 1> v8_test.cpp 1>c:\users\rti\desktop\v8\src\spaces-inl.h(188): warning C4800: 'uint32_t' : ブール値を 'true' または 'false' に強制的に設定します (警告の処理) 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: __thiscall v8::HandleScope::~HandleScope(void)" (??1HandleScope@v8@@QAE@XZ) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: __thiscall v8::String::AsciiValue::~AsciiValue(void)" (??1AsciiValue@String@v8@@QAE@XZ) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: __thiscall v8::String::AsciiValue::AsciiValue(class v8::Handle<class v8::Value>)" (??0AsciiValue@String@v8@@QAE@V?$Handle@VValue@v8@@@2@@Z) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: class v8::Local<class v8::Value> __thiscall v8::Script::Run(void)" (?Run@Script@v8@@QAE?AV?$Local@VValue@v8@@@2@XZ) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: static class v8::Local<class v8::Script> __cdecl v8::Script::Compile(class v8::Handle<class v8::String>,class v8::ScriptOrigin *,class v8::ScriptData *,class v8::Handle<class v8::String>)" (?Compile@Script@v8@@SA?AV?$Local@VScript@v8@@@2@V?$Handle@VString@v8@@@2@PAVScriptOrigin@2@PAVScriptData@2@0@Z) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: static class v8::Local<class v8::String> __cdecl v8::String::New(char const *,int)" (?New@String@v8@@SA?AV?$Local@VString@v8@@@2@PBDH@Z) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: static class v8::Persistent<class v8::Context> __cdecl v8::Context::New(class v8::ExtensionConfiguration *,class v8::Handle<class v8::ObjectTemplate>,class v8::Handle<class v8::Value>)" (?New@Context@v8@@SA?AV?$Persistent@VContext@v8@@@2@PAVExtensionConfiguration@2@V?$Handle@VObjectTemplate@v8@@@2@V?$Handle@VValue@v8@@@2@@Z) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: __thiscall v8::HandleScope::HandleScope(void)" (??0HandleScope@v8@@QAE@XZ) が関数 _main で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: void __thiscall v8::Context::Enter(void)" (?Enter@Context@v8@@QAEXXZ) が関数 "public: __thiscall v8::Context::Scope::Scope(class v8::Handle<class v8::Context>)" (??0Scope@Context@v8@@QAE@V?$Handle@VContext@v8@@@2@@Z) で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "public: void __thiscall v8::Context::Exit(void)" (?Exit@Context@v8@@QAEXXZ) が関数 "public: __thiscall v8::Context::Scope::~Scope(void)" (??1Scope@Context@v8@@QAE@XZ) で参照されました。 1>v8_test.obj : error LNK2019: 未解決の外部シンボル "private: static void __cdecl v8::V8::DisposeGlobal(class v8::internal::Object * *)" (?DisposeGlobal@V8@v8@@CAXPAPAVObject@internal@2@@Z) が関数 "public: void __thiscall v8::Persistent<class v8::Context>::Dispose(void)" (?Dispose@?$Persistent@VContext@v8@@@v8@@QAEXXZ) で参照されました。 1>c:\users\rti\documents\visual studio 2010\Projects\v8_test\Debug\v8_test.exe : fatal error LNK1120: 外部参照 11 が未解決です。 1> 1>ビルドに失敗しました。
はい、エラーでました。
リンクできない。。。
あー、そうでした。 v8.lib をリンクするのを忘れていました。
プロジェクトの設定を開き、 linkしてあげてください。
先ほどと同じくプロジェクトのプロパティを開きます。
次は、リンカーの設定で、「追加のライブラリディレクトリ」に、 V8 をビルドして結果できた v8.lib ファイルのパスを書き込みます。
私は、デスクトップの v8 ディレクトリでビルドしたので、C:\Users\rti\Desktop\V8\tools\visual_studio\Debug となりました。
次の v8.lib ファイルをリンカーに登録します。
先ほど設定したのはパスのディレクトリで、これから設定するのが v8.lib ライブラリの本体です。
リンカーの「入力」にある「追加の依存ファイル」というところに v8.lib を追加します。
lib は ; 区切りで複数のファイルを指定しています。末尾の法に v8.lib と記入してください。
さてさて、3度目の正直今度こそ、ビルドは通るでしょう。
やってみましょう。
わーまたエラーがでました。
1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__timeGetTime@0 が関数 "public: void __thiscall v8::internal::Time::SetToCurrentTime(void)" (?SetToCurrentTime@Time@internal@v8@@QAEXXZ) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__bind@12 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Bind(int)" (?Bind@Win32Socket@internal@v8@@UAE_NH@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__htons@4 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Bind(int)" (?Bind@Win32Socket@internal@v8@@UAE_NH@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__htonl@4 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Bind(int)" (?Bind@Win32Socket@internal@v8@@UAE_NH@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__listen@8 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Listen(int)const " (?Listen@Win32Socket@internal@v8@@UBE_NH@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__accept@12 が関数 "public: virtual class v8::internal::Socket * __thiscall v8::internal::Win32Socket::Accept(void)const " (?Accept@Win32Socket@internal@v8@@UBEPAVSocket@23@XZ) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__freeaddrinfo@4 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Connect(char const *,char const *)" (?Connect@Win32Socket@internal@v8@@UAE_NPBD0@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__connect@12 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Connect(char const *,char const *)" (?Connect@Win32Socket@internal@v8@@UAE_NPBD0@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__getaddrinfo@16 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Connect(char const *,char const *)" (?Connect@Win32Socket@internal@v8@@UAE_NPBD0@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__closesocket@4 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Shutdown(void)" (?Shutdown@Win32Socket@internal@v8@@UAE_NXZ) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__shutdown@8 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::Shutdown(void)" (?Shutdown@Win32Socket@internal@v8@@UAE_NXZ) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__send@16 が関数 "public: virtual int __thiscall v8::internal::Win32Socket::Send(char const *,int)const " (?Send@Win32Socket@internal@v8@@UBEHPBDH@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__recv@16 が関数 "public: virtual int __thiscall v8::internal::Win32Socket::Receive(char *,int)const " (?Receive@Win32Socket@internal@v8@@UBEHPADH@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__setsockopt@20 が関数 "public: virtual bool __thiscall v8::internal::Win32Socket::SetReuseAddress(bool)" (?SetReuseAddress@Win32Socket@internal@v8@@UAE_N_N@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__WSAStartup@8 が関数 "public: static bool __cdecl v8::internal::Socket::Setup(void)" (?Setup@Socket@internal@v8@@SA_NXZ) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__WSAGetLastError@0 が関数 "public: static int __cdecl v8::internal::Socket::LastError(void)" (?LastError@Socket@internal@v8@@SAHXZ) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__ntohs@4 が関数 "public: static unsigned short __cdecl v8::internal::Socket::NToH(unsigned short)" (?NToH@Socket@internal@v8@@SAGG@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__ntohl@4 が関数 "public: static unsigned int __cdecl v8::internal::Socket::NToH(unsigned int)" (?NToH@Socket@internal@v8@@SAII@Z) で参照されました。 1>v8.lib(platform-win32.obj) : error LNK2019: 未解決の外部シンボル __imp__socket@12 が関数 "public: __thiscall v8::internal::Win32Socket::Win32Socket(void)" (??0Win32Socket@internal@v8@@QAE@XZ) で参照されました。 1>c:\users\rti\documents\visual studio 2010\Projects\v8_test\Debug\v8_test.exe : fatal error LNK1120: 外部参照 19 が未解決です。
うー、ws2_32.lib と winmm.lib のライブラリに含まれる API を利用しているようです。
プロジェクトの設定を開き、ws2_32.lib と winmm.lib ライブラリを追加してあげてください。
;ws2_32.lib;winmm.lib; と追加する。 ; は区切り文字。
さてさて、今度こそビルドできるでしょうか。
1> 1>ビルドに成功しました。 1> 1>経過時間 00:00:01.26 ========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========
おー、ビルドできました。
あなたの予想に反してこのソースコードは動いているようです。
ソースが動いたよ。やったね、たえちゃん。
ついに、C++ に V8 engine を組み込むことに成功しました。
V8 で 自作関数
さて、もうちょっと複雑なサンプルをやってみましょう。
今度は、C++の関数を javascript (V8 engine) から呼び出してみようと思います。
説明を読まずに、 hello world のところに関数を追加してみましょう。
Handle<String> source = String::New("'Hello' + ', World!'"); ↓ Handle<String> source = String::New("plus( 1 , 1 )");
ソースーだとこんな感じですね。
#include <v8.h> using namespace v8; int main(int argc, char* argv[]) { // Create a stack-allocated handle scope. HandleScope handle_scope; // Create a new context. Persistent<Context> context = Context::New(); // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // Create a string containing the JavaScript source code. ////Handle<String> source = String::New("'Hello' + ', World!'"); Handle<String> source = String::New("plus( 1 , 1 )"); // Compile the source code. Handle<Script> script = Script::Compile(source); // Run the script to get the result. Handle<Value> result = script->Run(); // Dispose the persistent context. context.Dispose(); // Convert the result to an ASCII string and print it. String::AsciiValue ascii(result); printf("%s\n", *ascii); return 0; }
作ったから動かしてみましょう。
あれれ、動作はしましたが、なにやら問題が発生しているようです。
<unknown>:0: Uncaught ReferenceError: plus is not defined (null) 続行するには何かキーを押してください . . .
上から見ていきましょう。
<unknown>:0: Uncaught ReferenceError: plus is not defined
plus って定義がないから怒っていますね。
今回は、この plus 関数を C++ で定義して動作させるのが目標です。
(null)
これは、このjavascript を実行した結果が NULL になってしまったためです。
エラーが発生したため、この式をうまく評価できませんでした。
つまり、 plus って関数をどうにかして、定義してあげれば、問題☆解決となるわけです。
OKOK。
で、どうやって関数を定義するかというとドキュメントを流し読みすると、 global object な template とかをつくってあげればいいようです。
http://code.google.com/intl/ja/apis/v8/embed.html#templates
そんなの簡単だよっ
int main(int argc, char* argv[]) { //これがグローバルなテンプレートだだだだ!! Handle<ObjectTemplate> global = ObjectTemplate::New(); // Create a stack-allocated handle scope. HandleScope handle_scope; // Create a new context. Persistent<Context> context = Context::New(); // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // Create a string containing the JavaScript source code. ////Handle<String> source = String::New("'Hello' + ', World!'"); Handle<String> source = String::New("plus( 1 , 1 )"); // Compile the source code. Handle<Script> script = Script::Compile(source); // Run the script to get the result. Handle<Value> result = script->Run(); // Dispose the persistent context. context.Dispose(); // Convert the result to an ASCII string and print it. String::AsciiValue ascii(result); printf("%s\n", *ascii); return 0; } //注意:このコードは動作しません。
はい。できました。
では、実行してみましょう。
ぎゃあ、強制停止させられてしましました。
いったい何がダメだったんでしょうか。
//ブレークポイントでどかーん void OS::DebugBreak() { #ifdef _MSC_VER __debugbreak(); #else ::DebugBreak(); #endif }
呼び出し履歴をたどっていくと、何やら V8 engine内でやっている自己チェックに引っかかって、強制的に止めてくれたみたいです。
# # Fatal error in v8::HandleScope::CreateHandle() # Cannot create a handle without a HandleScope # 続行するには何かキーを押してください . . .
まーようするに、HandleScope ってやつを ObjectTemplate::New() 前に作らないとだめらしいのです。
ソースをちょこっと直してみます。
int main(int argc, char* argv[]) { //これを先に作る // Create a stack-allocated handle scope. HandleScope handle_scope; //これがグローバルなテンプレートだだだだ!! Handle<ObjectTemplate> global = ObjectTemplate::New(); //上に移動 // Create a stack-allocated handle scope. // HandleScope handle_scope; // Create a new context. Persistent<Context> context = Context::New(); // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // Create a string containing the JavaScript source code. ////Handle<String> source = String::New("'Hello' + ', World!'"); Handle<String> source = String::New("plus( 1 , 1 )"); // Compile the source code. Handle<Script> script = Script::Compile(source); // Run the script to get the result. Handle<Value> result = script->Run(); // Dispose the persistent context. context.Dispose(); // Convert the result to an ASCII string and print it. String::AsciiValue ascii(result); printf("%s\n", *ascii); return 0; }
前と変わらず plus という関数がないと怒られていますが、クラッシュすることはなくなりました。
ふー。
まー、こーゆーこともありますよ。
気を取り直して、自作関数を定義して呼び出してみます。
V8 に登録する関数はこんな感じで作ればいいみたいです。
//足し算を定義 Handle<Value> Plus(const Arguments& args) { //引数の長さチェック if (args.Length() <= 1) { //とりあえず引数が足りないときは undefined にする. return Undefined(); } //引数の内容を取得. unsigned int A = args[0]->Uint32Value(); unsigned int B = args[1]->Uint32Value(); //足し算を実行 unsigned int sum = A + B; //結果を返す return Uint32::New(sum); } int main(int argc, char* argv[]) { //これを先に作る // Create a stack-allocated handle scope. HandleScope handle_scope; //これがグローバルなテンプレートだだだだ!! Handle<ObjectTemplate> global = ObjectTemplate::New(); //自作関数 Plus を plus としてグローバルなテンプレートにセット global->Set(String::New("plus"), FunctionTemplate::New(Plus)); //上に移動 // Create a stack-allocated handle scope. // HandleScope handle_scope; // Create a new context. //Persistent<Context> context = Context::New(); Persistent<Context> context = Context::New(NULL,global); //ここで globalを渡す. // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // Create a string containing the JavaScript source code. ////Handle<String> source = String::New("'Hello' + ', World!'"); Handle<String> source = String::New("plus( 1 , 1 )"); // Compile the source code. Handle<Script> script = Script::Compile(source); // Run the script to get the result. Handle<Value> result = script->Run(); // Dispose the persistent context. context.Dispose(); // Convert the result to an ASCII string and print it. String::AsciiValue ascii(result); printf("%s\n", *ascii); return 0; }
ビルドして実行します。
画面に、1 + 1 の結果である 2 が表示されました。
そう、ついに、私たちは、自分で作った関数を V8 engine から呼び出すことができたのです。
V8 engine 上の関数を C++ から呼ぶ
さっきは、 javascript(V8 engzine) から C++ の関数を呼び出しましたが、逆に C++ から javascript(V8 engine)上の関数を呼び出してみたいと思います。
方法はいくつかあるみたいです。
まずは、 javascript を2つ書く方法です。
最初の javascript で関数を定義します。
次の javascript で、最初に定義した関数を呼び出します。
int main(int argc, char* argv[]) { //これを先に作る // Create a stack-allocated handle scope. HandleScope handle_scope; //これがグローバルなテンプレートだだだだ!! Handle<ObjectTemplate> global = ObjectTemplate::New(); // Create a stack-allocated handle scope. // HandleScope handle_scope; // Create a new context. //Persistent<Context> context = Context::New(); Persistent<Context> context = Context::New(NULL,global); //ここで globalを渡す. // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); //掛け算をする関数を作る. Handle<String> source = String::New("function mul(a , b ){ return a * b; }"); // Compile the source code. Handle<Script> script = Script::Compile(source); //まずは動作させる。 Handle<Value> result = script->Run(); //もう一度ソースをつくって、 mul を呼び出してみる。 Handle<String> source2 = String::New("mul(10,2)"); //コンパイルして実行する. Handle<Script> script2 = Script::Compile(source2); Handle<Value> result2 = script2->Run(); // Dispose the persistent context. context.Dispose(); // Convert the result"2" to an ASCII string and print it. String::AsciiValue ascii(result2); printf("%s\n", *ascii); return 0; }
実行すると、 10 * 2 = 20 なので、 20 と表示されます。
うまくいきました。
ソースコードに書いてある通りですが、最初の javascript 部分で関数 mul を定義だけします。
//掛け算をする関数を作る. Handle<String> source = String::New("function mul(a , b ){ return a * b; }"); // Compile the source code. Handle<Script> script = Script::Compile(source); //まずは動作させる。 Handle<Value> result = script->Run();
次の javascript で "mul(10,2)" という文字列としてソースコードを与えて実行しています。 evalみたいな感じでしょうか。
//もう一度ソースをつくって、 mul を呼び出してみる。 Handle<String> source2 = String::New("mul(10,2)"); //コンパイルして実行する. Handle<Script> script2 = Script::Compile(source2); Handle<Value> result2 = script2->Run();
ともあれ、これで動作はしました。
もう一つの方法を見ていきます。
もうひとつの方法は、 V8 engine に呼び出す関数へのポインタみたいなものを教えてもらって呼び出す方法です。
int main(int argc, char* argv[]) { //これを先に作る // Create a stack-allocated handle scope. HandleScope handle_scope; //これがグローバルなテンプレートだだだだ!! Handle<ObjectTemplate> global = ObjectTemplate::New(); // Create a stack-allocated handle scope. // HandleScope handle_scope; // Create a new context. //Persistent<Context> context = Context::New(); Persistent<Context> context = Context::New(NULL,global); //ここで globalを渡す. // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // Create a string containing the JavaScript source code. ////Handle<String> source = String::New("'Hello' + ', World!'"); //掛け算をする関数 Handle<String> source = String::New("function mul(a , b ){ return a * b; }"); // Compile the source code. Handle<Script> script = Script::Compile(source); //まずは動作させる。 // Run the script to get the result. Handle<Value> result = script->Run(); // // //もう一度ソースをつくって、 mul を呼び出してみる。 // Handle<String> source2 = String::New("mul(10,2)"); // Handle<Script> script2 = Script::Compile(source2); // Handle<Value> result2 = script2->Run(); // //関数を取得して呼び出してみる。 //mul関数を取得. Local<Function> mulFunction = Local<Function>::Cast( context->Global()->Get(String::New("mul")) ); //引数を作成する Handle<Value> args[] = { String::New("10"),String::New("2") }; //実行する. Handle<Value> result2 = mulFunction->Call(mulFunction, 2 , args); // Dispose the persistent context. context.Dispose(); // Convert the result to an ASCII string and print it. String::AsciiValue ascii(result2); printf("%s\n", *ascii); return 0; }
これも実行すると、 10 * 2 = 20 で、20という値が表示されます。
最初にある javascript で mulという関数を定義しているところまでは一緒です。
このあと、文字列ではなく、 V8 engine に対して問い合わせを行い mul 関数を呼び出しているところが違います。
//mul関数を取得. Local<Function> mulFunction = Local<Function>::Cast( context->Global()->Get(String::New("mul")) );
この部分で、 mul という変数のをよこせと命令しています。
そして、それは Function型だとして、 Local
//引数を作成する Handle<Value> args[] = { String::New("10"),String::New("2") }; //実行する. Handle<Value> result2 = mulFunction->Call(mulFunction, 2 , args);
そして、ここで関数の引数 args を作成し、 mul関数を呼び出します。
こんな風にしても、 javascript の関数を C++ から呼び出すことができました。
さて、次は、と書いたところでタイムオーバーになってしまいました。
次は不定期に書いていきたいと思います。
それと、V8 のテストディレクトリの中にテストコードがたくさん入っているので、これを見ながら掘り下げていけばいろいろできるらしいです。
今回参考にしたテストファイル
V8\test\cctest\test-api.cc
オンラインで見たい人はこちらから(重いので注意)
参考文献
[v8-users] Re: Call a JS function like a C function?
http://www.mail-archive.com/v8-users@googlegroups.com/msg01065.html
V8ドキュメント日本語訳 ゆとり
http://sites.google.com/site/bibourokdesu/v8dokyumento-nihongo-yaku
http://sites.google.com/site/bibourokdesu/embedder-s-guide
Embedder's Guide - V8 JavaScript Engine
http://javascript.g.hatena.ne.jp/edvakf/20100407/1270626241
Google V8 JavaScript Engine を使ったゲーム開発 (pdf)
http://www.tom.sfc.keio.ac.jp/~fjedi/zengeren/07/07_v8forpdf.pdf
第7回 全日本学生ゲーム開発者連合(全ゲ連)ハッシュタグまとめ2
http://togetter.com/li/55752
Using V8 - Google's Chrome JavaScript Virtual Machine
http://www.codeproject.com/KB/library/Using_V8_Javascript_VM.aspx
パフォーマンス比較 Lua vs V8 Engine いい勝負
http://shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=lua&lang2=v8
パフォーマンス比較 Lua JIT vs V8 Engine Lua JITすげー
http://shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=luajit&lang2=v8