Hatena::ブログ(Diary)

お前の血は何色だ!! 4 このページをアンテナに追加 RSSフィード Twitter

2010-12-15

人の話を聞かない人の V8 エンジン。 V8 を C++ に組み込んで遊ぼう

C++ Advent Calendar です。

googleV8 engine を 組み込んで簡易スクリプトにしてしまおうという話です。


C++の言語仕様や boost の話は超絶詳しい人たちがされているので、誰もやらなそうな ぬるいいい加減な話をします。

#間違ってたら突っ込みよろしく!!


なぜ V8Lua ぢゃだめなんですか?

C++ から呼び出しやすい 組み込み言語として、 Luaがあります。

しかし、 Luaはコメントが -- から始まったり、 〜end と、

C / C++ に組み込みやすいスクリプト言語なのになぜか pascal チックな文法?という不思議なところがあります。


やはり、 C++ ユーザとしてはコメントは // で書きたいし、ブロックは { } でやりたいですよね (y/Yes)


そんなところに、 googleV8 engine があります。

C++に組み込めます。 速い(らしい)です。javascript です。 javascript のコメントは // です。ブロックは { } です。

これは使ってみるしかありません。


ここでは、 V8 engine + VS2010 という不思議な組み合わせで、 V8 engine を使う方法を簡単に説明したいと思います。

V8 build は省略。

ソースを落として、ビルドする、、、、は、前に「厨房でもできる windows VS2010 で V8 engine をビルド 」で書きましたので、そちらを参考にしてください。

#Linux などの Unix 系のユーザーだったら、もっと楽に構築できると思いますがww

V8hello world!

ここでは、初心者向けに少しづつ説明していきます。(自分が試行錯誤した結果の記録です)。わかっている人は読み飛ばしてください。


さて、

V8 Engine をビルドできたとします。


f:id:rti7743:20101216025740j:image

ビルドした v8.lib のファイルができているとします。


これを利用して hello world をやってみます。


新規に VS2010 でプロジェクトを作ります。

f:id:rti7743:20101216025741j:image

名前は v8_test とか適当に決めます。


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 のパスを通すのをすっかり忘れていました。

左側のプロジェクトエクスプローラーのところで右クリックし、プロパティを開きます。

f:id:rti7743:20101216025742j:image

プロジェクトのプロパティページを開き、「追加のインクルードディレクトリ」 の項目に、V8 engine の src ディレクトリを指定します。

f:id:rti7743:20101216025743j:image

私は、デスクトップの上に作った v8 というフォルダでビルドしたので、C:\Users\rti\Desktop\V8\src と、なりました。


include したいのはこの v8.h ファイルです。

f:id:rti7743:20101216025744j:image


さて、追加のインクルードディレクトリもできました。

よし。もう一度ビルドしてみましょう。

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 ファイルのパスを書き込みます。

f:id:rti7743:20101216025745j:image


私は、デスクトップv8 ディレクトリビルドしたので、C:\Users\rti\Desktop\V8\tools\visual_studio\Debug となりました。


次の v8.lib ファイルをリンカーに登録します。

先ほど設定したのはパスのディレクトリで、これから設定するのが v8.lib ライブラリの本体です。


リンカーの「入力」にある「追加の依存ファイル」というところに v8.lib を追加します。

lib は ; 区切りで複数のファイルを指定しています。末尾の法に v8.lib と記入してください。

f:id:rti7743:20101216025746j:image



さてさて、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; と追加する。 ; は区切り文字。

f:id:rti7743:20101216025747j:image



さてさて、今度こそビルドできるでしょうか。

1>
1>ビルドに成功しました。
1>
1>経過時間 00:00:01.26
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========

おー、ビルドできました


f:id:rti7743:20101216025748j:image

あなたの予想に反してこのソースコードは動いているようです。

ソースが動いたよ。やったね、たえちゃん。


ついに、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;
}

作ったから動かしてみましょう。

あれれ、動作はしましたが、なにやら問題が発生しているようです。

f:id:rti7743:20101216025749j:image


<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;
}
//注意:このコードは動作しません。



はい。できました。

では、実行してみましょう。


f:id:rti7743:20101216025750j:image

ぎゃあ、強制停止させられてしましました。

いったい何がダメだったんでしょうか。


//ブレークポイントでどかーん
void OS::DebugBreak() {
#ifdef _MSC_VER
  __debugbreak();
#else
  ::DebugBreak();
#endif
}

呼び出し履歴をたどっていくと、何やら V8 engine内でやっている自己チェックに引っかかって、強制的に止めてくれたみたいです。

f:id:rti7743:20101216025752j:image



画面を見てみると、このように表示されていました。

f:id:rti7743:20101216025751j:image

#
# 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;
}

はい。

今度はちゃんと動きました。

f:id:rti7743:20101216025753j:image


前と変わらず 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 から呼び出すことができたのです。

f:id:rti7743:20101216025754j:image


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 と表示されます。

うまくいきました。

f:id:rti7743:20101216025755j:image


ソースコードに書いてある通りですが、最初の 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という値が表示されます。

f:id:rti7743:20101216025755j:image


最初にある javascript で mulという関数を定義しているところまでは一緒です。

このあと、文字列ではなく、 V8 engine に対して問い合わせを行い mul 関数を呼び出しているところが違います。

  //mul関数を取得.
  Local<Function> mulFunction = Local<Function>::Cast( context->Global()->Get(String::New("mul")) );

この部分で、 mul という変数のをよこせと命令しています。

そして、それは Function型だとして、 Local<Function>::Cast と キャストを行っています。


  //引数を作成する
  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

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/rti7743/20101215/1292437763