How to pass a wrapped C++ object to a Javascript callback? - c++

I'm trying to write a Node.js module, using C++, that wraps and exposes some classes from libhdf5.
I'm currently interested in two classes from libhdf5. The first one is File, and it opens an hdf5 file. The second one is Group, and it represents groups within that file. You get Group objects from a File object.
I've written some code in which I create a File object and attempt to get a Group from it. I am trying to make my Node.js module as JavaScripty as possible, so I want to return the group using a callback. So, I am trying to code my module so that it's used like this:
var hdf5 = require('hdf5');
var file = new hdf5.File('/tmp/example.h5');
file.getGroup('foobar', function (err, group) { console.log(group); });
So, in the C++ code for my File wrapper, I'd have a function that maps to the getGroup function here, and it'd call the given anonymous function, passing in any errors as well as the new Group object wrapper.
Given that this sounded to me like what the Node.js documentation shows to be a factory of wrapped objects, I have modeled my Group code after the examples there.
So, I have my Group wrapper coded up, but am stuck trying to instantiate it. I don't know enough yet to know how to stray away from using the v8 Arguments class for function parameters. Because of that, I can't seem to be able to pass in some parameters that I need for my v8 persistent constructor function (because I am instantiating this from C++, and not from JS-land).

You are almost there. You don't need to pass Arguments to Group::Instantiate. Just pass what you need and use the constructor to create the new instance of Group. For example:
Handle<Value> Group::Instantiate(const std::string& name) {
HandleScope scope;
Local<v8::Value> argv[1] = {
Local<v8::Value>::New(String::New(name.c_str()))
};
return scope.Close(Constructor->NewInstance(1, argv));
}
The method Group::New does the rest of the construction work.
Handle<Value> Group::New(const Arguments& args) {
HandleScope scope;
if (!args[0]->IsString()) {
return ThrowException(Exception::TypeError(String::New("First argument must be a string")));
}
const std::string name(*(String::Utf8Value(args[0]->ToString())));
Group * const group = new Group(name);
bar->Wrap(args.This());
return args.This();
}
In File::OpenGroup you can do this:
Handle<Value> File::OpenGroup (const Arguments& args) {
HandleScope scope;
if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsFunction()) {
ThrowException(Exception::SyntaxError(String::New("expected name, callback")));
return scope.Close(Undefined());
}
const std::string name(*(String::Utf8Value(args[0]->ToString())));
Local<Function> callback = Local<Function>::Cast(args[1]);
const unsigned argc = 2;
Local<Value> argv[argc] = {
Local<Value>::New(Null()),
Local<Value>::New(Group::Instantiate(name))
};
callback->Call(Context::GetCurrent()->Global(), argc, argv);
return scope.Close(Undefined());
}

Related

How to inject a value known only at runtime using boost-di?

I am learning to use boost::di and I fail to understand how one is expected to create multiple instances of the same type using different runtime parameters.
From what I understand, The documentation mostly focuses on creating a single object with the same parameters. I would like to create multiple objects using parameters that are known at runtime.
For example, I have the following type:
struct Device
{
int slot_;
Device(int slot) : slot_(slot) {}
};
The slot variable is determined only at runtime via user input.
Ideally, I would like to be able to do something like this:
auto device1 = injector.create<std::shared_ptr<Device>>(123); // slot = 123
auto device2 = injector.create<std::shared_ptr<Device>>(321); // slot = 321
Based on the documentation, This is what I managed so far:
auto injector = di::make_injector(
di::bind<Device>.to(
[&](const auto& injector)
{
// this assumes slot is a captured by the lambda
return std::make_shared<Device>(slot);
})
);
int slot = 123; // captured by lambda
auto device1 = injector.create<std::shared_ptr<Device>>();
slot = 321; // captured by lambda
auto device2 = injector.create<std::shared_ptr<Device>>();
I would like to avoid capturing the slot variable by the lambda. My real use case involves many runtime parameters for each type.
Is there a more elegant way to pass the runtime parameters to the injector at runtime?
What is the generic way to pass runtime parameters to constructors with boost::di?

How to implement a property enumerator for a C++ native object in V8?

I'm writing a Node.js C++ module that communicates with a database which returns BSON objects, and wrap them in a V8 object so they can be accessed from the JavaScript.
For that I create an ObjectTemplate, and configure it through SetHandler. My getter and setter work, but the property enumerator does not return anything. Here is the code:
void PropertyEnumerator(const PropertyCallbackInfo<Array>& info)
{
// Enumerator, aka `Object.keys(obj)`
auto isolate = info.GetIsolate();
auto data = unrwap_internal_field<BsonObjectData>(info.Holder(), 0);
Local<Array> array = Array::New(isolate);
int i = 0;
bson_iter_t iter;
bson_iter_init_from_data(&iter, data->document_data, data->document_length);
while (bson_iter_next(&iter)) {
const char* key = bson_iter_key(&iter);
array->Set(i++, String::NewFromUtf8(isolate, key, v8::NewStringType::kNormal).ToLocalChecked());
}
info.GetReturnValue().Set(array);
}
I have checked that the function is actually called
I have checked that the array is populated with the right values
But in the JavaScript when I do console.log(Object.keys(obj)) I get an empty array. It's like info.GetReturnValue().Set(array); does not do anything. The documentation states that each element of the array must be a Name, and String inherits from Name so I really don't understand.
Thoughts?
As #jmrk suggested in their comment, the problem was that I didn't implement the query function for my object template. This made the enumerator work properly:
void PropertyQuery(Local<Name> property, const PropertyCallbackInfo<Integer>& info)
{
info.GetReturnValue().Set(PropertyAttribute::None);
}

How to pass the second parameter of ObjectTemplate::New in Google V8?

I know creating an ObjectTemplate and we can do several things to it. But my question is not about those well-known things.
I want to know how to pass the second parameter.
As the official guide said:
Each function template has an associated object template. This is used to configure objects created with this function as their constructor.
And the second parameter of ObjectTemplate::New is a constructor typed by FunctionTemplate.
static Local<ObjectTemplate> New(Isolate *isolate, Local<FunctionTemplate> constructor = Local<FunctionTemplate>());
That means something like this:
void Constructor(const FunctionCallbackInfo<Value>& args)
{
// ...
}
Local<FunctionTemplate> _constructor = FunctionTemplate::New(isolate, Constructor);
Local<ObjectTemplate> tpl = ObjectTemplate::New(isolate, _constructor);
Who can give me a demo that how to implement the Constructor function.
I tried this, but failed:
void Constructor(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
args.This()->Set(String::NewFromUtf8(isolate, "value"), Number::New(isolate, 233));
args.GetReturnValue().Set(args.This());
}
By the way, I know the use case of accessors and so on, I just want to know how to use the second parameter.
There's an example for the second ObjectTemplate::New parameter in V8's API tests at https://chromium.googlesource.com/v8/v8/+/master/test/cctest/test-api.cc#1901:
LocalContext env;
Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
v8::Local<v8::String> class_name = v8_str("the_class_name");
fun->SetClassName(class_name);
Local<ObjectTemplate> templ1 = ObjectTemplate::New(isolate, fun);
templ1->Set(isolate, "x", v8_num(10));
templ1->Set(isolate, "value", v8_num(233)); // From your last snippet.
Local<v8::Object> instance1 =
templ1->NewInstance(env.local()).ToLocalChecked();
CHECK(class_name->StrictEquals(instance1->GetConstructorName()));
As you can see, there's no need to implement property creation indirectly via a FunctionTemplate, that's what the ObjectTemplate is for. See the "x" and "value" properties in the above example.
The quote you mentioned refers to something else. When you instantiate a function from a FunctionTemplate, then JavaScript code can use that function as a constructor. The mentioned ObjectTemplate can be used to configure the objects that will be created that way.

How do I store a reference to a function so I can call it back later in a node.js C++ addon module?

Here is a node.js addon module I've written in C++ and built using node-gyp.
When StoreFunction I am trying to store a pointer to the function so I can use it later
When I try to invoke it later though in InvokeFunction I get a Segmentation fault. What baffled me if I examined the pointer in both functions (using cout) they are the same value.
So I'm guessing either the change of invoking context changes between calling the two functions or I don't understand what I'm pointing to.
All (ummmmmm) pointers gratefully received on my problem here..............
#include <node.h>
#include <v8.h>
using namespace v8;
v8::Persistent<v8::Function> callbackFunction;
Handle<Value> StoreFunction(const Arguments& args) {
HandleScope scope;
callbackFunction = *Local<Function>::Cast(args[0]);
return scope.Close(Undefined());
}
Handle<Value> InvokeFunction(const Arguments& args) {
HandleScope scope;
Local<Value> argv[1] = { String::New("Callback from InvokeFunction")};
callbackFunction->Call(Context::GetCurrent()->Global(), 1, argv);
return scope.Close(Undefined());
}
void init(Handle<Object> target) {
NODE_SET_METHOD(target, "StoreFunction", StoreFunction);
NODE_SET_METHOD(target, "InvokeFunction", InvokeFunction);
}
NODE_MODULE(someaddonmodule, init);
And of course some calling js...........
var myaddon = require('../build/Release/someaddonmodule');
myaddon.StoreFunction(function(data){
console.log("Called back: "+data);
});
myaddon.InvokeFunction(); //causes a segmentation fault
The answer is because we're not programming in Java any more Toto.
The pointer I created is pointing at the Local Handle, rather than the function. Holding a 'reference' to this isn't enough to stop the V8 garbage collection destroying it when the scope closes.
To deal with this an explicit request needs to be made to V8 to put aside some memory to hold
the function which done like this :
Persistent< Function > percy;
Local<Function> callbackFunction = Local<Function>::Cast(args[0]);
percy = Persistent<Function>::New(callbackFunction);
If anyone with a better understanding of V8 internals knows more than this, I'd still really like to hear your explanation :)

Callback requires const function, how to pass instance member of object

I am using WebKitGTK+ in a larger GTKmm/C++ application. I am using JavaScriptCore to interact with the WebKitWebFrame and JSContext within.
I am stuck now as I need to interact with a GTK GUI component when a javascript function is called. To this end I found the JSObjectMakeFunctionWithCallback function.
JSStringRef str = JSStringCreateWithUTF8CString("ClickCallback");
JSObjectRef func = JSObjectMakeFunctionWithCallback(m_jsRef, str, ClickCallback);
JSObjectSetProperty(m_jsRef, JSContextGetGlobalObject(m_jsRef), str, func, kJSPropertyAttributeNone, NULL);
JSStringRelease(str);
Where the callback must be defined as a static function with def:
static JSValueRef ClickCallback(JSContextRef ctx, JSObjectRef func, JSObjectRef self, size_t argc, const JSValueRef argv[], JSValueRef* exception)
So everything is working except I need my object instance in the callback to get back at the GUI component I need to manipulate.
There are tons of similar questions on SO but most focus on passing the object instance into the callback. I can not see a way of doing that with this API.
Any ideas?
The callback is required to be a pointer to a free function, so no amount of magic can get you to pass a member function directly. One common solution is to make an intermediate global free function that holds the object instance:
JSValueRef ClickCallback(...)
{
Foo & obj = getInstance(); // implement this somehow
return obj.click(...);
}
Alternatively, you can make this a static member function of your GUI class. The main point is that you must obtain the instance reference separately and call the member function yourself if you have to pass a plain function pointer to your API.