您的位置:首页 > Web前端 > JavaScript

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

2016-11-22 15:04 381 查看


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

up
vote18down
votefavorite
8

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;

}


c++ v8 embedded-v8
shareimprove
this question
edited Jul
10 '12 at 22:59

asked Jul 8 '12 at 21:53





user396404
1,15151933

 
 
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 
add
a comment


2 Answers

activeoldestvotes

up vote11down
voteaccepted
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;
}


shareimprove
this answer
edited Jul
10 '12 at 19:51

answered Jul 8 '12 at 23:58





Nate Kohl
18.4k73346

 
 
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
add
a comment
up vote2down
vote
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.

shareimprove
this answer
answered Jun 4 '13 at 17:50





jkp
33.7k188395

 
 
v8::ScriptCompiler::CompileFunctionInContext does the "wrap your code in a function" bit for you. – xaxxonOct
11 at 21:19
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: