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);
这适用于任何无状态的JavaScript函数或引用全局C ++变量(即std::cout
)。但是,如果我们希望我们的全局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有办法做一些功能相同的东西,但我发现自己因为v8::FunctionTemplate
的Doxygen而感到困惑。尝试类似事情的人是否愿意将这个过程提炼成更容易理解的东西?我还想学习如何创建绑定到C ++对象的现有非全局实例的JavaScript对象的全局实例。
答案 0 :(得分: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;
}