nan icon indicating copy to clipboard operation
nan copied to clipboard

[RFC] Provide facilities for JS<->C++ conversion

Open hokein opened this issue 8 years ago • 1 comments

Background

I have written some node addons, and I feel kind of pain in converting JS to C++ and back in the API bindings. So I plan to write some utility APIs that can help me do the conversion stuff automatically (one time and save forever). I think Nan is the best place for these APIs rather than creating a new separate module (and more developers will benefit from it ;)).

Here is a proposal to provide new JS<->C++ facilities in NAN that make it easier to marshal types between JavaScript and C++.

Feedback and comments are welcome. If the community accepts the proposal, I'm happy to contribute it :-).

Problem

When writing a native node addon, developers need to write some defending code in their API bindings:

  1. Check whether the passed arguments are valid (e.g. the numbers of arguments, the type of arguments are expected).
  2. If they are valid, then convert them from V8 types to native C++ types (using raw V8 APIs or Nan APIs).

Although Nan has already provided two helpers Nan::To and Nan::New, which converts V8 types to C++ types and back, but they don't fully solve the problem:

  • Only basic native number types (bool, uint32, int32, int64, double) are supported.
  • Developers still need to check availability for each argument separately.

Proposed Solution

This proposal is to add two functions in Nan which would make it easier to marshal types between JavaScript and C++, so that developers won't need to re-implement their JS<->C++ bindings.

The APIs are defined below:

// Currently Supported template types:
//   * basic native types: int32, int64, bool, double, uint32
//   * std::string
//   * std::vector<T>
// We could provide some interfaces allowing developers to support their own C++ types.
// 
// Convert V8's arguments to C++ native types. Returns false if fails.
// 
// An example of a JS function "add(a, b)" which takes two Integer parameters.
//   NAN_METHOD(Add) {
//     int a, b;
//     if (FromV8Argument(info, &a, &b)) {
//       // Do anything with a, b.
//     }
//   }
template<typename... Args>
bool FromV8Argument(Nan::NAN_METHOD_ARGS_TYPE v8_args, Args*... args);

// Convert the supported C++ native types to V8 types.
// Similar to NAN::New(), but it supports more types.
template<typename T>
v8::Local<v8::Value> ToV8Value(T value);

With the help of these two functions, writing JS bindings could be easier, and most of the messy stuff of converting V8 types to C++ types is done automatically and carefully.

The original code without using FromV8Argument:

NAN_METHOD(add) {
  if (!info[0]->IsNumber() || !info[1]->IsNumber()) {
    Nan::ThrowTypeError("Wrong arguments");
    return;
  }
  double arg0 = info[0]->NumberValue();
  double arg1 = info[1]->NumberValue();
  info.GetReturnValue().Set(Nan::New(arg0 + arg1)); 
}

The code using FromV8Argument:

NAN_METHOD(add) {
  double arg0, arg1;
  if (!Nan::FromV8Arguments(info, &arg0, &arg1)) {
    Nan::ThrowTypeError("Wrong arguments");
    return;
  }
  info.GetReturnValue().Set(Nan::New(arg0 + arg1)); 
}

Again, suggestions are welcome.

hokein avatar Jul 30 '17 14:07 hokein

In general, we try not to have pure utility functionality in NAN, save for what already is there. Nan::To and Nan::New are not just utility, they are actually necessary for things to work on all supported versions. Another problem is that C++11 features like variadic templates cannot be used for NAN's support of Node pre-4.

You might want to take a look at https://github.com/charto/nbind. I think it does what you proposed and much more in a very elegant way.

On July 30, 2017 4:42:37 PM GMT+02:00, Haojian Wu [email protected] wrote:

Background

I have written some node addons, and I feel kind of pain in converting JS to C++ and back in the API bindings. So I plan to write some utility APIs that can help me do the conversion stuff automatically (one time and save forever). I think Nan is the best place for these APIs rather than creating a new separate module (and more developers will benefit from it ;)).

Here is a proposal to provide new JS<->C++ facilities in NAN that make it easier to marshal types between JavaScript and C++.

Feedback and comments are welcome. If the community accepts the proposal, I'm happy to contribute it :-).

Problem

When writing a native node addon, developers need to write some defending code in their API bindings:

  1. Check whether the passed arguments are valid (e.g. the numbers of arguments, the type of arguments are expected).
  2. If they are valid, then convert them from V8 types to native C++ types (using raw V8 APIs or Nan APIs).

Although Nan has already provided two helpers Nan::To and Nan::New, which converts V8 types to C++ types and back, but they don't fully solve the problem:

  • Only basic native number types (bool, uint32, int32, int64, double) are supported.
  • Developers still need to check availability for each argument separately.

Proposed Solution

This proposal is to add two functions in Nan which would make it easier to marshal types between JavaScript and C++, so that developers won't need to re-implement their JS<->C++ bindings.

The APIs are defined below:

// Currently Supported template types:
//   * basic native types: int32, int64, bool, double, uint32
//   * std::string
//   * std::vector<T>
// We could provide some interfaces allowing developers to support
their own C++ types.
// 
// Convert V8's arguments to C++ native types. Returns false if fails.
// 
// An example of a JS function "add(a, b)" which takes two Integer
parameters.
//   NAN_METHOD(Add) {
//     int a, b;
//     if (FromV8Argument(info, &a, &b)) {
//       // Do anything with a, b.
//     }
//   }
template<typename... Args>
bool FromV8Argument(Nan::NAN_METHOD_ARGS_TYPE v8_args, Args*... args);

// Convert the supported C++ native types to V8 types.
// Similar to NAN::New(), but it supports more types.
template<typename T>
v8::Local<v8::Value> ToV8Value(T value);

With the help of these two functions, writing JS bindings could be easier, and most of the messy stuff of converting V8 types to C++ types is done automatically and carefully.

The original code without using FromV8Argument:

NAN_METHOD(add) {
 if (!info[0]->IsNumber() || !info[1]->IsNumber()) {
   Nan::ThrowTypeError("Wrong arguments");
   return;
 }
 double arg0 = info[0]->NumberValue();
 double arg1 = info[1]->NumberValue();
 info.GetReturnValue().Set(Nan::New(arg0 + arg1)); 
}

The code using FromV8Argument:

NAN_METHOD(add) {
 double arg0, arg1;
 if (!Nan::FromV8Arguments(info, &arg0, &arg1)) {
   Nan::ThrowTypeError("Wrong arguments");
   return;
 }
 info.GetReturnValue().Set(Nan::New(arg0 + arg1)); 
}

Again, suggestions are welcome.

kkoopa avatar Jul 30 '17 15:07 kkoopa