Вызов функции javascript v8 из С++ с аргументом
Я работаю с С++ и v8 и сталкиваюсь с следующей задачей: я хочу иметь возможность определить функцию в javascript с помощью v8, а затем вызвать функцию позже через С++. Кроме того, я хочу иметь возможность передать аргумент функции javascript из С++. Я думаю, что следующий примерный исходный код объяснит это лучше всего. Посмотрите на конец кода примера, чтобы узнать, чего я пытаюсь выполнить.
#include <v8.h>
#include <iostream>
#include <string>
#include <array>
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();
Context::Scope context_scope(context);
Handle<String> source;
Handle<Script> script;
Handle<Value> result;
// Create a string containing the JavaScript source code.
source = String::New("function test_function(test_arg) { var match = 0;if(test_arg[0] == test_arg[1]) { match = 1; }");
// Compile the source code.
script = Script::Compile(source);
// What I want to be able to do (this part isn't valid code..
// it just represents what I would like to do.
// An array is defined in c++ called pass_arg,
// then passed to the javascript function test_function() as an argument
std::array< std::string, 2 > pass_arg = {"value1", "value2"};
int result = script->callFunction("test_function", pass_arg);
}
Любые советы?
UPDATE:
Основываясь на приведенном совете, я смог собрать следующий код. Он был протестирован и работает:
#include <v8.h>
#include <iostream>
#include <string>
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();
//context->AllowCodeGenerationFromStrings(true);
// Enter the created context for compiling and
// running the hello world script.
Context::Scope context_scope(context);
Handle<String> source;
Handle<Script> script;
Handle<Value> result;
// Create a string containing the JavaScript source code.
source = String::New("function test_function() { var match = 0;if(arguments[0] == arguments[1]) { match = 1; } return match; }");
// Compile the source code.
script = Script::Compile(source);
// Run the script to get the result.
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);
Handle<v8::Object> global = context->Global();
Handle<v8::Value> value = global->Get(String::New("test_function"));
Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
Handle<Value> args[2];
Handle<Value> js_result;
int final_result;
args[0] = v8::String::New("1");
args[1] = v8::String::New("1");
js_result = func->Call(global, 2, args);
String::AsciiValue ascii(js_result);
final_result = atoi(*ascii);
if(final_result == 1) {
std::cout << "Matched\n";
} else {
std::cout << "NOT Matched\n";
}
return 0;
}
Ответы
Ответ 1
Я не тестировал это, но возможно, что что-то вроде этого будет работать:
// ...define and compile "test_function"
Handle<v8::Object> global = context->Global();
Handle<v8::Value> value = global->Get(String::New("test_function"));
if (value->IsFunction()) {
Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
Handle<Value> args[2];
args[0] = v8::String::New("value1");
args[1] = v8::String::New("value2");
Handle<Value> js_result = func->Call(global, 2, args);
if (js_result->IsInt32()) {
int32_t result = js_result->ToInt32().Value();
// do something with the result
}
}
Edit:
Похоже, ваша функция javascript ожидает один аргумент (состоящий из массива из двух значений), но похоже, что мы вызываем func
, передавая два аргумента.
Чтобы проверить эту гипотезу, вы можете изменить свою функцию javascript, чтобы принять два аргумента и сравнить их, например:
function test_function(test_arg1, test_arg2) {
var match = 0;
if (test_arg1 == test_arg2) {
match = 1;
} else {
match = 0;
}
return match;
}
Ответ 2
Еще один более простой способ:
Handle<String> code = String::New(
"(function(arg) {\n\
console.log(arg);\n\
})");
Handle<Value> result = Script::Compile(code)->Run();
Handle<Function> function = Handle<Function>::Cast(result);
Local<Value> args[] = { String::New("testing!") };
func->Call(Context::GetCurrent()->Global(), 1, args);
По существу компилируйте код, который возвращает анонимную функцию, а затем вызывайте это с любыми аргументами, которые вы хотите передать.
Ответ 3
Для новой версии v8 вы можете использовать v8::Object::CallAsFunction
или v8::Function::Call
для вызова функции javascript. Вот пример для последней версии (7.4.x)
#include <iostream>
#include <libplatform/libplatform.h>
#include <v8.h>
int main(int argc, char* argv[])
{
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
v8::Isolate::CreateParams createParams;
createParams.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(createParams);
std::cout << v8::V8::GetVersion() << std::endl;
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, "var foo=function(){return 'foo get called';}");
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
v8::TryCatch tryCatch(isolate);
v8::MaybeLocal<v8::Value> result = script->Run(context);
if (result.IsEmpty()) {
v8::String::Utf8Value e(isolate, tryCatch.Exception());
std::cerr << "Exception: " << *e << std::endl;
} else {
v8::String::Utf8Value r(isolate, result.ToLocalChecked());
std::cout << *r << std::endl;
}
v8::Local<v8::Value> foo_value = context->Global()->Get(v8::String::NewFromUtf8(isolate, "foo"));
if (foo_value->IsFunction()) {
v8::Local<v8::Value> foo_ret = foo_value->ToObject(isolate)->CallAsFunction(context, context->Global(), 0, nullptr).ToLocalChecked();
v8::String::Utf8Value utf8Value(isolate, foo_ret);
std::cout << "CallAsFunction result: " << *utf8Value << std::endl;
v8::Local<v8::Object> foo_object = foo_value->ToObject(isolate);
v8::Local<v8::Value> foo_result = v8::Function::Cast(*foo_object)->Call(context, context->Global(), 0, nullptr).ToLocalChecked();
std::cout << "Call result: " << *(v8::String::Utf8Value(isolate, foo_result)) << std::endl;
} else {
std::cerr << "foo is not a function" << std::endl;
}
}
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete createParams.array_buffer_allocator;
return EXIT_SUCCESS;
}