【问题标题】:Calling a v8 javascript function from c++ with an argument使用参数从 c++ 调用 v8 javascript 函数
【发布时间】:2012-07-08 21:53:11
【问题描述】:

我正在使用 c++ 和 v8,并且遇到了以下挑战:我希望能够使用 v8 在 javascript 中定义一个函数,然后稍后通过 c++ 调用该函数。此外,我希望能够将参数从 c++ 传递给 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);

}

有什么建议吗?

更新:

根据给出的建议,我已经能够整理出以下代码。它已经过测试并且可以工作:

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

}

【问题讨论】:

  • 我假设 IsInt32 返回 true,但 Int32Value 返回 0?
  • 看看我的编辑——也许我们没有传递足够的参数...
  • 您的代码中有一个错误:您在使用当前上下文后处置了它。您必须将 dispose 行放在程序的末尾。

标签: c++ v8 embedded-v8


【解决方案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
    }
}

编辑:

看起来您的 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; 
}

【讨论】:

  • 它似乎正在工作。不过,我在使用 js_result 时遇到了麻烦。它说 if(js_result.IsInt32) 的部分在编译时给出以下错误:错误:'class v8::Handle<:value>' has no member named 'Int32'|
  • @user396404: 可以试试js_result-&gt;IsInt32() 吗?
  • 确实有效。代码编译,但即使值匹配,它也不返回值 1:/
  • 感谢所有帮助。事实证明,我必须使用参数数组而不是 test_arg1 和 test_arg2 来引用变量。对于将来阅读本文的任何人,它说 Call(global, 2, args) 的部分,2 表示 args 数组的长度。我在原始答案中发布了修改后的工作代码。感谢大家的帮助!
  • 有机会获得此代码的更新版本吗?它似乎无法与 2018 年的 V8 一起编译。
【解决方案2】:

对于新版本的 v8,您可以使用 v8::Object::CallAsFunctionv8::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;
}

【讨论】:

    【解决方案3】:

    另一种更简单的方法如下:

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

    基本上是编译一些返回匿名函数的代码,然后用你想要传递的任何参数调用它。

    【讨论】:

    • v8::ScriptCompiler::CompileFunctionInContext 为您执行“将代码包装在函数中”位。
    • 有机会获得此代码的更新版本吗?它似乎无法与 2018 年的 V8 一起编译。
    猜你喜欢
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-20
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2013-09-07
    相关资源
    最近更新 更多