5

Google 的 v8 文档描述了如何将全局函数添加到 JavaScript 上下文。我们可以使用 C++11 中的新 lambda 特性轻松实现类似 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++ 变量呢?例如,假设我们正在创建几个不同的 JavaScript 上下文,每个上下文都有自己的print使用不同 C++ 的全局函数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 迷惑了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 回答