5

Google の v8 ドキュメントには、グローバル関数を JavaScript コンテキストに追加する方法が説明されています。C++11 の新しいラムダ機能を使用して、printf のような関数を非常に簡単に実装できます。

Handle<ObjectTemplate> global = ObjectTemplate::New();
global->Set(String::New("print"), FunctionTemplate::New(
[](const v8::Arguments &args) -> v8::Handle<v8::Value>
{
  v8::String::AsciiValue ascii(args[0]);
  std::cout << *ascii << "\n";
} ));
Persistent<Context> context = Context::New(NULL, global);

これは、ステートレスであるか、グローバル C++ 変数 (つまりstd::cout) を参照するグローバル JavaScript 関数でうまく機能します。しかし、グローバル JavaScript 関数が非グローバル C++ 変数を参照するようにしたい場合はどうすればよいでしょうか? たとえば、それぞれがprint異なる C++ を使用する独自のグローバル関数を持ついくつかの異なる JavaScript コンテキストを作成しているとしstd::ostreamます。v8 関数テンプレートstd::functionが関数ポインターの代わりにオブジェクトを使用する場合、次のようにします。

Persistent<Context> create_context(std::ostream &out)
{
  Handle<ObjectTemplate> global = ObjectTemplate::New();
  global->Set(String::New("print"), FunctionTemplate::New(
  [&out](const v8::Arguments &args) -> v8::Handle<v8::Value>
  {
    v8::String::AsciiValue ascii(args[0]);
    out << *ascii << "\n";
  } ));
  return Context::New(NULL, global);
}

残念ながら、v8 はこれをサポートしていないようです。v8には機能的に同等の方法があると思います(希望しますか?)が、Doxygen for v8::FunctionTemplate. 似たようなことを試みたことのある人は、そのプロセスをより理解しやすいものに要約することをいとわないでしょうか? また、C++ オブジェクトの既存の非グローバル インスタンスにバインドされた JavaScript オブジェクトのグローバル インスタンスを作成する方法も学びたいと思います。

4

1 に答える 1

6

私自身の質問に答えて...重要なのは、v8::Argumentsが単なる引数の配列ではないことを理解することです。また、非常に便利なメソッドも含まれていCallee()ますData()。関数がJavaScriptオブジェクトのメソッドでCallee()ある場合、メソッドが呼び出されたそのオブジェクトのインスタンスを取得するために使用できると思います。次に、有用な状態情報をオブジェクトインスタンスに格納できます。void*オブジェクトに関数テンプレートを追加するときに、を介して任意のC++オブジェクトを指すことができるデータハンドルを指定することもできます。Data()この関数固有のデータハンドルには、メソッドを介してアクセスできます。

以下は、を使用した質問で私がやろうとしていたことのかなり完全な例ですv8::Arguments::Data()。うまくいけば、これは似たようなことをしたい人に役立つでしょう。あなたが好きな代替戦略を持っているなら(そして私はこれを行う方法が複数あると確信しています)、別の答えにそれを自由に追加してください!

#include <iostream>
#include <ostream>
#include <v8.h>

// add print() function to an object template
void add_print(v8::Handle<v8::ObjectTemplate>& ot, std::ostream* out)
{
  // add function template to ot
  ot->Set(v8::String::New("print"), v8::FunctionTemplate::New(
    // parameter 1 is the function callback (implemented here as a lambda)
    [](const v8::Arguments& args)->v8::Handle<v8::Value>
    {
      // recover our pointer to an std::ostream from the
      // function template's data handle
      v8::Handle<v8::External> data = v8::Handle<v8::External>::Cast(args.Data());
      std::ostream* out = static_cast<std::ostream*>(data->Value());

      // verify that we have the correct number of function arguments
      if ( args.Length() != 1 )
        return v8::ThrowException(v8::String::New("Too many arguments to print()."));

      // print the ascii representation of the argument to the output stream
      v8::String::AsciiValue ascii(args[0]);
      *out << *ascii << "\n";

      // like 'return void;' only in JavaScript
      return v8::Undefined();
    },

    // parameter 2 is the data handle with the pointer to an std::ostream
    v8::External::New(out)
  ));
}

int main()
{
  // create a stack-allocated handle scope
  v8::HandleScope handle_scope;

  // create a global template
  v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New();

  // add a print() function using std::cout to the global template
  add_print(global, &std::cout);

  // create a context
  v8::Persistent<v8::Context> context = v8::Context::New(nullptr, global);

  // enter the created context
  v8::Context::Scope context_scope(context);

  // create a string containing the JavaScript source code
  v8::Local<v8::String> source = v8::String::New("print('1 + 1 = ' + (1 + 1));");

  // compile the source code
  v8::Local<v8::Script> script = v8::Script::Compile(source);

  // run the script
  script->Run();

  // dispose of the persistent context
  context.Dispose();

  return 0;
}
于 2012-12-22T21:40:22.560 に答える