Вызов javascript-функции v8 из c ++ с аргументом

Я работаю с c ++ и v8 и столкнулся со следующей проблемой: я хочу иметь возможность определять функцию в javascript с помощью v8, а затем вызывать функцию позже через c ++. Кроме того, я хочу иметь возможность передавать аргумент функции javascript из c ++. Я думаю, что следующий пример исходного кода объяснит это лучше всего. Посмотрите в конце примера кода, чтобы узнать, чего я пытаюсь достичь.

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

}

Какие-нибудь советы?

ОБНОВЛЕНИЕ:

Основываясь на данном совете, мне удалось составить следующий код. Протестировано и работает:

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

}

person user396404    schedule 08.07.2012    source источник
comment
Я предполагаю, что IsInt32 возвращает true, а Int32Value возвращает 0?   -  person Nate Kohl    schedule 10.07.2012
comment
Посмотрите мою правку - может быть, мы не передаем достаточно параметров ...   -  person Nate Kohl    schedule 10.07.2012
comment
У вас есть ошибка в вашем коде: вы избавляетесь от текущего контекста и после того, как используете его. Вы должны поместить строку удаления в конце вашей программы.   -  person banuj    schedule 05.06.2013


Ответы (3)


Я не тестировал это, но возможно, что что-то вроде этого сработает:

// ...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
    }
}

Изменить:

Похоже, ваша функция 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; 
}
person Nate Kohl    schedule 08.07.2012
comment
Кажется, это работает. Однако у меня проблемы с использованием js_result. Часть, в которой говорится, что if (js_result.IsInt32) выдает следующую ошибку во время компиляции: error: ‘class v8 :: Handle‹ v8 :: Value ›’ не имеет члена с именем ‘Int32’ | - person user396404; 10.07.2012
comment
@ user396404: может, попробовать js_result->IsInt32()? - person Nate Kohl; 10.07.2012
comment
Это сработало. Код компилируется, но не возвращает значение 1, даже если значения совпадают: / - person user396404; 10.07.2012
comment
Спасибо за помощь. Оказывается, мне пришлось ссылаться на переменные, используя массив аргументов вместо test_arg1 и test_arg2. Для тех, кто будет читать это в будущем, часть, где написано Call (global, 2, args), 2 представляет длину массива args. Я опубликовал исправленный и рабочий код в своем исходном ответе. Спасибо за помощь! - person user396404; 11.07.2012
comment
Есть ли шанс получить обновленную версию этого кода? Похоже, что он не совместим с V8 в 2018 году. - person Eugen; 03.11.2018

В новой версии 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;
}
person alijandro    schedule 04.06.2019

Другой более простой метод выглядит следующим образом:

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);

По сути, скомпилируйте код, который возвращает анонимную функцию, а затем вызовите ее с любыми аргументами, которые вы хотите передать.

person jkp    schedule 04.06.2013
comment
v8 :: ScriptCompiler :: CompileFunctionInContext превращает ваш код в бит функции за вас. - person xaxxon; 12.10.2016
comment
Есть ли шанс получить обновленную версию этого кода? Похоже, что он не совместим с V8 в 2018 году. - person Eugen; 03.11.2018