Native Node.JS module - parsing an int[] from arguments - c++

I am trying to write a native C++ module to include in a Node.js project -- I followed the guide here and have things setup pretty well.
The general idea is that I want to pass an array of integers to my C++ module to be sorted; the module then returns the sorted array.
However, I cannot compile using node-gyp build because I hit the following error:
error: no viable conversion from 'Local' to 'int *'
It is complaining about this code in my C++:
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
int* inputArray = args[0]; // <-- ERROR!
sort(inputArray, 0, sizeof(inputArray) - 1);
args.GetReturnValue().Set(inputArray);
}
This all makes conceptual sense to me -- the compiler can't magically cast arg[0] (presumably of type v8::Local) to an int*. Having said that, I cannot seem to find any way to get my argument successfully cast into a C++ integer array.
It should be known that my C++ is rather rusty, and I know next-to-nothing about V8. Can anyone point me in the right direction?

It's not trivial: you first need to unpack the JS array (internally represented as a v8::Array) into something sortable (like a std::vector), sort it, and convert it back to a JS array.
Here's an example:
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// Make sure there is an argument.
if (args.Length() != 1) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Need an argument")));
return;
}
// Make sure it's an array.
if (! args[0]->IsArray()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "First argument needs to be an array")));
return;
}
// Unpack JS array into a std::vector
std::vector<int> values;
Local<Array> input = Local<Array>::Cast(args[0]);
unsigned int numValues = input->Length();
for (unsigned int i = 0; i < numValues; i++) {
values.push_back(input->Get(i)->NumberValue());
}
// Sort the vector.
std::sort(values.begin(), values.end());
// Create a new JS array from the vector.
Local<Array> result = Array::New(isolate);
for (unsigned int i = 0; i < numValues; i++ ) {
result->Set(i, Number::New(isolate, values[i]));
}
// Return it.
args.GetReturnValue().Set(result);
}
Disclaimer: I'm not a v8 wizard, nor a C++ one, so there may be better ways to do this.

Related

How to convert an V8 Array to std::vector<std::string>? [duplicate]

I want to access the elements of an array that is passed in a function as an arg from the js side.
The code is like this:
void Method(const FunctionCallbackInfo<Value> &args){
Isolate* isolate = args.GetIsolate();
Local<Array> array = Local<Array>::Cast(args[0]);
for(int i=0;i<(int)array->Length();i++){
auto ele = array->Get(i);
}
I'm getting this error:
error: no matching function for call to ‘v8::Array::Get(int&)’
After reading the implementation of the V8 Array and I get to know that there is no Get method for Array.
Here is the implementation of Array in the v8 source code:
class V8_EXPORT Array : public Object {
public:
uint32_t Length() const;
/**
* Creates a JavaScript array with the given length. If the length
* is negative the returned array will have length 0.
*/
static Local<Array> New(Isolate* isolate, int length = 0);
/**
* Creates a JavaScript array out of a Local<Value> array in C++
* with a known length.
*/
static Local<Array> New(Isolate* isolate, Local<Value>* elements,
size_t length);
V8_INLINE static Array* Cast(Value* obj);
private:
Array();
static void CheckCast(Value* obj);
};
I'm new to v8 lib. I walked through some tutorials and it was working fine for them. Can anyone help me to figure out what's wrong with it? If we can't use Local<Array> then what else is there which can fulfill this purpose?
Hard to answer without knowing which exact version of v8 you're targeting, but in the current doxygen documentation there are two overloads for v8::Object::Get:
MaybeLocal< Value > Get (Local< Context > context, Local< Value > key)
MaybeLocal< Value > Get (Local< Context > context, uint32_t index)
So I think you can do the following:
Local<Context> ctx = isolate->GetCurrentContext();
auto ele = array->Get(ctx, i);
...

Nodejs c++ addon: Not able to access Array elements

I want to access the elements of an array that is passed in a function as an arg from the js side.
The code is like this:
void Method(const FunctionCallbackInfo<Value> &args){
Isolate* isolate = args.GetIsolate();
Local<Array> array = Local<Array>::Cast(args[0]);
for(int i=0;i<(int)array->Length();i++){
auto ele = array->Get(i);
}
I'm getting this error:
error: no matching function for call to ‘v8::Array::Get(int&)’
After reading the implementation of the V8 Array and I get to know that there is no Get method for Array.
Here is the implementation of Array in the v8 source code:
class V8_EXPORT Array : public Object {
public:
uint32_t Length() const;
/**
* Creates a JavaScript array with the given length. If the length
* is negative the returned array will have length 0.
*/
static Local<Array> New(Isolate* isolate, int length = 0);
/**
* Creates a JavaScript array out of a Local<Value> array in C++
* with a known length.
*/
static Local<Array> New(Isolate* isolate, Local<Value>* elements,
size_t length);
V8_INLINE static Array* Cast(Value* obj);
private:
Array();
static void CheckCast(Value* obj);
};
I'm new to v8 lib. I walked through some tutorials and it was working fine for them. Can anyone help me to figure out what's wrong with it? If we can't use Local<Array> then what else is there which can fulfill this purpose?
Hard to answer without knowing which exact version of v8 you're targeting, but in the current doxygen documentation there are two overloads for v8::Object::Get:
MaybeLocal< Value > Get (Local< Context > context, Local< Value > key)
MaybeLocal< Value > Get (Local< Context > context, uint32_t index)
So I think you can do the following:
Local<Context> ctx = isolate->GetCurrentContext();
auto ele = array->Get(ctx, i);
...

Node-Addon-Api Pass Array As Function Argument

I am trying to create a basic native node addon where a javascript array is passed from node and then processed in c++. The problem is I cannot figure out how to correctly pass the array. I can instantiate the array without issue but assigning it using info[0].as throws errors.
My c++ code is
#include <napi.h>
using namespace Napi;
using namespace std;
Value Add(const CallbackInfo& info)
{
Env env = info.Env();
Array result = Napi::Array::New(env);
Array a = info[0].As<Array>;
double arg1 = info[1].As<Number>().DoubleValue();
Number num = Napi::Number::New(env, 2 + arg1);
return num;
}
The error I am getting is
../cppsrc/main.cpp: In function ‘Napi::Value Add(const Napi::CallbackInfo&)’:
../cppsrc/main.cpp:12:21: error: conversion from ‘<unresolved overloaded function type>’ to non-scalar type ‘Napi::Array’ requested
Array a = info[0].As<Array>;
~~~~~~~~^~~~~~~~~
What is the correct way to pass an array to c++? Is it even possible?
This works for me:
void runSetPropertyAsyncWorker(const CallbackInfo& info)
{
std::string path = info[0].As<Napi::String>();
int property = info[1].As<Number>();
int dataSize = info[2].As<Number>();
Array b = info[3].As<Array>();
for(int i = 0; i<b.Length(); i++)
{
Napi::Value v = b[i];
if (v.IsNumber())
{
int value = (int)v.As<Napi::Number>();
...
...
}
}
...
...
Function callback = info[4].As<Function>();
AsyncWorker* asyncWorker = new SetPropertyAsyncWorker(callback, ...);
asyncWorker->Queue();
}
Use Napi::Object. Napi::Array is actually inherited from Napi::Object.
You could replace the code Array a = info[0].As<Array>; with Array a = info[0].ToObject();.
You can then access the data members via Value
operator[] (uint32_t index) const
Source: https://nodejs.github.io/node-addon-api/class_napi_1_1_object.html
Edit: Bonus feature, if an argument that is not an object is passed, this will automatically trigger an Error: Object Expected.
error code here:
Array a = info[0].As<Array>;
which should be
Array a = info[0].As<Array>();
I was not able to find a solution to the actual question of interacting with the Javascript object directly with node-addon-api. The Solution that I chose to go with is JSON.stringify any arrays or objects and then parse then in c++ is a library called rapid json. This proves to be the only way to interface with javascript objects that i've been able to find

Make certain local variables available to LLVM pass

I have the following situation.
I need to insert certain CallInst in my code.
The prototype of my function is
void __llvmFooBar(int data, int loc);
The parameter loc is generated by the pass. I don't need any external data to generate that.
But, the parameter data is basically calculated by using some variable declared inside my C-code.
//test.c
int main()
{
int k = 0;
happyEnding(k);
}
void happyEnding(int k)
{
int __data = seed(time()) + k%2;
//callInst to be inserted here, it should be
//__llvmFooBar(__data, 23);
myAlgorithim();
}
//pass.cpp
......
std::vector<Value *> args(2);
args[0] = ??? //need help here
args[1] = ConstantInt::get(IntegerTy, getLoc());
m_builder->CreateCall(hook, args);
In generic sense, how to make all variable with specific naming convention(like starts with __) make available to llvm pass ?

Dynamic function calls at runtime (va_list)

There is a way in C to obtain a dynamic length argument list with va_list, as described here:
http://www.cprogramming.com/tutorial/c/lesson17.html
That quite simple, but most times in C++ not needed. I am currently building a top level wrapper class for encapsulating some Zend functionality.
Anyway, I do need to make a call to such a dynamic function like printf from a normal function dynamically.
I mean the reverse way of the example described above, here is waht I got so war, maybe this explains my idea a little better:
void ZendParams::setRequired( int &Var )
{
// save every var pointer to a stack
// and save the type with it. (if needed, does not seems to be)
// after call to ZendParams::fetch()
// the values are stored in the passed variables
this->_params.push_back( (void*) &Var );
this->_paramStr.append( 'i' );
}
size_t ZendParams::fetch()
{
if ( zend_parse_parameters(
ZEND_NUM_ARGS() TSRMLS_CC, ,
this->_paramStr.c_str(),
...
) !== FAILURE)
{
}
return 0;
}
So I want to make the call to zend_parse_parameters dynamically.
The base idea is to add pointer to vars in a list and pass them to the zend function (as reference) dynamically.
I thought there must be a way to do this with va_list , too.
But how?
To get it more simple, I am using this example:
list<int> myList;
myList.push_back(1);
myList.push_back(5);
myList.push_back(10);
myList.push_back(37);
myList.push_back(42);
double function avg( int num, ... )
va_list arguments;
int sum = 0;
va_start ( arguments, num );
for ( int x = 0; x < num; x++ )
{
sum += va_arg ( arguments, int );
}
va_end ( arguments );
return sum / num;
}
I want to call avg with all numbers I got in the list. I took the example function from the tutorial mentioned above, but it should show up what I mean in a very simple way.
However, I can not change the function called, as it is part of the zend framework.
Is there any way in C or C++ to do this?
My 2nd approach:
template <typename... Arguments>
size_t Zend::getMethodParams( string paramStr, Arguments... Args )
{
if ( zend_parse_parameters(
ZEND_NUM_ARGS() TSRMLS_CC, ,
paramStr.c_str(),
Args...
) == FAILURE)
{
return 0;
}
}
To be called like this (to get the defined 3 Parameters):
string My1stParam;
int My2ndParam;
int My3rdParam;
Zend::getMethodParams<string, int, int>( "sii", &My1stParam, &My2ndParam, &My3rdParam );
I think that should work, but there is one hard issue with that:
The zend_parse_parameters function returns 2 values for a string (c-style string!):
- a char pointer and
- the string length.
So I would either have to call it that way:
char* My1stParam_str;
int My1stParam_length
int My2ndParam;
int My3rdParam;
Zend::getMethodParams<string, int, int>( "sii", &My1stParam_str, &My1stParam_length, &My2ndParam, &My3rdParam );
string My1stParam;
My1stParam.assign(My1stParam_str, My1stParam_length);
Anyway, that was what I wanted to prevent.
Or I would have to modify the list of arguments passed to the zend_parse_parameters function to do these additional steps internally.
I am hoping to be able to call it at least in that way:
string My1stParam;
int My2ndParam;
int My3rdParam;
Zend::getMethodParams<string, int, int>( "sii", &My1stParam, &My2ndParam, &My3rdParam );
So say this clearly: The parameters are known at compile time, but the function call will be very different within all occurrences in the later sourcecode.
I found a way around this within the zend framework. I did indeed search for such a solution before, but it seems to be not very good documented ( n.m. already mentoined, that there is no zend internal way like a va_list taking function ).
But there is indeed one!
For everyone else stucking with this problem:
long arg;
zval ***args;
int i, argc = ZEND_NUM_ARGS( );
if (zend_parse_parameters(1 TSRMLS_CC, "l", &arg) == FAILURE) return;
array_init(return_value);
add_index_long(return_value, 0, arg);
if(argc>1) {
args = (zval ***)emalloc(argc * sizeof(zval **));
if(zend_get_parameters_array_ex(argc, args) == FAILURE) {
efree(args);
return;
}
for(i = 1; i < argc; i++) {
zval_add_ref(args[i]);
add_index_zval(return_value,i, *args[i]);
}
efree(args);
}
That is the solution ;)
This snippet, found on http://docstore.mik.ua/orelly/webprog/php/ch14_07.htm parses all parameters dynamically into the c representation of an PHP array.