Calling a v8 javascript function from c++ with an argument

本文介绍如何在 C++ 中使用 V8 引擎定义并调用 JavaScript 函数,并传递参数。通过示例代码展示了如何正确地创建、编译及运行 JavaScript 代码,并从 C++ 调用这些函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

I am working with c++ and v8, and have run into the following challenge: I want to be able to define a function in javascript using v8, then call the function later on through c++. Additionally, I want to be able to pass an argument to the javascript function from c++. I think the following sample source code would explain it best. Check towards the end of the sample code to see what I am trying to accomplish.

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

}

Any tips?

UPDATE:

Based on the advice given, I have been able to put together the following code. It has been tested and works:

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

}
share improve this question
 
 
I assume that IsInt32 returns true, but Int32Value returns 0? –  Nate Kohl  Jul 10 '12 at 19:36
 
Check out my edit -- maybe we're not passing enough parameters... –  Nate Kohl  Jul 10 '12 at 19:52
 
You have a mistake in your code: you dispose the current context and after you are using it. You must put the dispose line at the end of your program. –  banuj  Jun 5 '13 at 8:20 

2 Answers

up vote 11 down vote accepted

I haven't tested this, but it's possible that something like this will work:

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

It looks like your javascript function expects a single argument (consisting of an array of two values), but it kinda looks like we're calling func by passing in two arguments.

To test this hypothesis, you could change your javascript function to take two arguments and compare them, e.g.:

function test_function(test_arg1, test_arg2) { 
  var match = 0; 
  if (test_arg1 == test_arg2) { 
    match = 1; 
  } else { 
    match = 0; 
  } 
  return match; 
}
share improve this answer
 
 
It seems to be working. I'm having trouble using js_result though. The part where it says if(js_result.IsInt32) is giving the following error during compile time: error: ‘class v8::Handle<v8::Value>’ has no member named ‘Int32’| –  user396404  Jul 10 '12 at 0:36 
1 
@user396404: maybe try js_result->IsInt32() instead? –  Nate Kohl  Jul 10 '12 at 11:26
 
That did work. The code compiles, but it is not returning a value of 1 even if the values match :/ –  user396404  Jul 10 '12 at 19:20
 
Thanks for all the help. It turns out I had to reference the variables using the arguments array, instead of test_arg1 and test_arg2. For anyone reading this in the future, the part where it says Call(global, 2, args), the 2 represents the length of the args array. I posted the revised and working code in my original answer. Thanks for all the help! –  user396404  Jul 10 '12 at 23:00

Another simpler method is as follows:

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

Essentially compile some code that returns an anonymous function, then call that with whatever arguments you want to pass.

share improve this answer
 
 
v8::ScriptCompiler::CompileFunctionInContext does the "wrap your code in a function" bit for you. –  xaxxon Oct 11 at 21:19
### 函数调用中的原型缺失问题 在C/C++编程中,如果尝试调用未声明其原型的函数,则可能会遇到编译器警告或错误。当编译器无法找到函数定义时,默认情况下会假设该函数返回`int`类型并接受任意数量和类型的参数[^2]。 这种默认行为可能导致潜在的风险: - 参数传递不匹配:实际传入的参数与预期不符。 - 返回值处理不当:接收方可能按照错误的数据类型解析返回结果。 - 编译优化受限:由于缺乏具体信息,某些级别的优化措施难以实施。 #### 解决方案一:提前声明函数原型 最直接有效的方法是在源文件顶部或者头文件内增加相应的函数前向声明语句。例如,在`.h`文件中加入如下代码片段可以解决问题: ```cpp // myfunctions.h #ifndef MYFUNCTIONS_H_ #define MYFUNCTIONS_H_ void someFunction(int param); #endif //MYFUNCTIONS_H_ ``` 随后确保所有需要用到此功能的地方都包含了这个头文件即可正常工作。 #### 解决方案二:在同一翻译单元中定义函数 另一种方式是将被调用者的实现放置于调用者之前的位置。即把整个函数体写到第一次出现它的地方前面去。不过这种方法降低了模块化程度,并不利于大型项目的维护和发展。 对于上述提到的情况——栈帧结构图显示了不同层次间存在异常处理器设置差异的情形下[A][^1],即使某一层级缺少显式的SEH(Structured Exception Handling)机制支持也不会影响其他部分正常的执行流程;而对于原生接口绑定操作[B]以及序列化过程[C][^3]而言,保持良好的编码习惯如始终提供完整的函数签名同样重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值