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.
Related
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?
Ok first off I'm very new to C++ so apologies if my understanding is poor. I'll try explain myself as best I can. What I have is I am using a library function that returns a std::shared_ptr<SomeObject>, I then have a different library function that takes a raw pointer argument (more specifically node-addon-api Napi::External<T>::New(Napi::Env env, T *data) static function). I want to create a Napi::External object using my std::shared_ptr. What I am currently doing is this:
{
// ...
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(info.Env(), pSomeObject.get());
auto instance = MyNapiObjectWrapper::Create({ ext });
return instance;
}
But I am worried this will run into memory issues.
My pSomeObject only exists in the current scope, so I imagine what should happen is after the return, it's reference count will drop to 0 and the SomeObject instance it points to will be destroyed and as such I will have issues with the instance I return which uses this object. However I have been able to run this code and call functions on SomeObject from my instance, so I'm thinking maybe my understanding is wrong.
My question is what should I do when given a shared pointer but I need to work off a raw pointer because of other third party library requirements? One option that was proposed to me was make a deep copy of the object and create a pointer to that
If my understanding on any of this is wrong please correct me, as I said I'm quite new to C++.
============================
Edit:
So I was missing from my original post info about ownership and what exactly this block is. The block is an instance method for an implementation I have for a Napi::ObjectWrap instance. This instance method needs to return an Napi::Object which will be available to the caller in node.js. I am using Napi::External as I need to pass a sub type of Napi::Value to the constructor New function when creating the Napi:Object I return, and I need the wrapped SomeObject object in the external which I extract in my MyNapiObjectWrapper constructor like so:
class MyNapiObjectWrapper
{
private:
SomeObject* someObject;
static Napi::FunctionReference constructor; // ignore for now
public:
static void Init(Napi::Env env) {...}
MyNapiObjectWrapper(const CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
// My original code to match the above example
this->someObject = info[0].As<const Napi::External<SomeObject>>().Data();
}
DoSomething()
{
this->someObject->DoSomething();
}
}
I have since come to realise I can pass the address of the shared pointer when creating my external and use it as follows
// modified first sample
{{
// ...
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(info.Env(), &pSomeObject);
auto instance = MyNapiObjectWrapper::Create({ ext });
return instance;
}
// modified second sample
class MyNapiObjectWrapper
{
private:
std::shared_ptr<SomeObject> someObject;
static Napi::FunctionReference constructor; // ignore for now
public:
static void Init(Napi::Env env) {...}
MyNapiObjectWrapper(const CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
// My original code to match the above example
this->someObject =
*info[0].As<const Napi::External<std::shared_ptr<SomeObject>>>().Data();
}
DoSomething()
{
this->someObject->DoSomething();
}
}
So now I am passing a pointer to a shared_ptr to create my Napi::External, my question now though is this OK? Like I said at the start I'm new to c++ but this seems like a bit of a smell. However I tested it with some debugging and could see the reference count go up, so I'm thinking I'm in the clear???
Here the important part of the documentation:
The Napi::External template class implements the ability to create a Napi::Value object with arbitrary C++ data. It is the user's responsibility to manage the memory for the arbitrary C++ data.
So you need to ensure that the object passed by data to Napi::External Napi::External::New exits until the Napi::External<T> object is destructed.
So the code that you have shown is not correct.
What you could do is to pass a Finalize callback to the New function:
static Napi::External Napi::External::New(napi_env env,
T* data,
Finalizer finalizeCallback);
And use a lambda function as Finalize, that lambda could hold a copy through the capture to the shared pointer allowing to keep the shared pointer alive until finalize is called.
std::shared_ptr<SomeObject> pSomeObject = something.CreateSomeObject();
auto ext = Napi::External<SomeObject>::New(
info.Env(),
pSomeObject.get(),
[pSomeObject](Env /*env*/, SomeObject* data) {});
A short purely technical question:
If I have an uncertain amount of overlapping (time-wise) instances of the class below. Is it and how is it ensured, that the "this" in "call_back_when_done" belongs to the same "this" as it was in "Start"?
class MyClass{
int ident = -1;
bool ready = false;
void Start(string url){
ident = aStaticClass.DoSomethingAndForkThread(url, callback_when_done);
}
void call_back_when_done(){
ready = true;
}
}
e.g.:
for (int i=0; i < 3; i++)
new MyClass().Start(<aURL>);
Thank You
At first, you can bind the function to "this" like described here using currying : (How) is it possible to bind/rebind a method to work with a delegate of a different signature?
I would prefer a lambda function for your example case like described here : C# Lambdas and "this" variable scope
Lambda functions are bound to the scope of the "this" context where they are created. Members of your surrounding Class are automatically visible to the Lambda function. Using a Lambda function you'll get shorter code which can also better optimized by the compiler.
It is guaranteed.
When you pass callback_when_done to DoSomethingAndForkThread in Start, you are not only passing the raw function pointer (like you would in C++ with &MyClass::callback_when_done, but some kind of tuple consisting of the method to call and the object on which the method should be called (this).
If you like it more explicit you can also write a closure manually:
void Start(string url) {
var that = this; // that get's captured by the closure
ident = aStaticClass.DoSomethingAndForkThread(url, () => that.callback_when_done());
}
I have working code where I can create as many Point objects as I want, but it re-creates the object template each time the constructor is called, which seems like it's probably wrong.
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
// make the Point constructor function available to JS
global_templ->Set(v8::String::NewFromUtf8(isolate, "Point"), FunctionTemplate::New(isolate, v8_Point));
and then the constructor itself:
void v8_Point(const v8::FunctionCallbackInfo<v8::Value>& args) {
HandleScope scope(args.GetIsolate());
// this bit should probably be cached somehow
Local<ObjectTemplate> point_template = ObjectTemplate::New(args.GetIsolate());
point_template->SetInternalFieldCount(1);
point_template->SetAccessor(String::NewFromUtf8(args.GetIsolate(), "x"), GetPointX, SetPointX);
point_template->SetAccessor(String::NewFromUtf8(args.GetIsolate(), "y"), GetPointY, SetPointY);
// end section to be cached
Local<Object> obj = point_template->NewInstance();
Point * p = new Point(1,1);
obj->SetInternalField(0, External::New(args.GetIsolate(), p));
args.GetReturnValue().Set(obj);
}
But it seems like I should be able to pass in the point_template object instead of re-creating it each time. I saw that there's a Data() field in args, but that only allows for a Value type and an ObjectTemplate is of type Template, not Value.
Any help on the right way to do this would be greatly appreciated.
I figured it out finally.
In javascript, when you add a function via a FunctionTemplate and then call it as a constructor (e.g. new MyFunction), then in your c++ callback the args.This() will be a new object created by the using the FunctionTemplate's InstanceTemplate object template.
// Everything has to go in a single global template (as I understand)
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
// create the function template and tell it the callback to use
Local<FunctionTemplate> point_constructor = FunctionTemplate::New(isolate, v8_Point);
// set the internal field count so our actual c++ object can tag along
// with the javascript object so our accessors can use it
point_constructor->InstanceTemplate()->SetInternalFieldCount(1);
// associate getters and setters for the 'x' field on point
point_constructor->InstanceTemplate()->SetAccessor(String::NewFromUtf8(isolate, "x"), GetPointX, SetPointX);
... add any other function and object templates to the global template ...
// add the global template to the context our javascript will run in
Local<Context> x_context = Context::New(isolate, NULL, global_templ);
Then, for the actual function:
void v8_Point(const v8::FunctionCallbackInfo<v8::Value>& args) {
// (just an example of a handy utility function)
// whether or not it was called as "new Point()" or just "Point()"
printf("Is constructor call: %s\n", args.IsConstructCall()?"yes":"no");
// create your c++ object that will follow the javascript object around
// make sure not to make it on the stack or it won't be around later when you need it
Point * p = new Point();
// another handy helper function example
// see how the internal field count is what it was set to earlier
// in the InstanceTemplate
printf("Internal field count: %d\n",args.This()->InternalFieldCount()); // this prints the value '1'
// put the new Point object into the internal field
args.This()->SetInternalField(0, External::New(args.GetIsolate(), p));
// return the new object back to the javascript caller
args.GetReturnValue().Set(args.This());
}
Now, when you write the getter and setter, you have access to your actual c++ object in the body of them:
void GetPointX(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
Local<Object> self = info.Holder();
// This is where we take the actual c++ object that was embedded
// into the javascript object and get it back to a useable c++ object
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<Point*>(ptr)->x_; //x_ is the name of the field in the c++ object
// return the value back to javascript
info.GetReturnValue().Set(value);
}
void SetPointX(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<void>& info) {
Local<Object> self = info.Holder();
// same concept here as in the "getter" above where you get access
// to the actual c++ object and then set the value from javascript
// into the actual c++ object field
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<Point*>(ptr)->x_ = value->Int32Value();
}
Almost all of this came from here: https://developers.google.com/v8/embed?hl=en#accessing-dynamic-variables
except it doesn't talk about the proper way to make your objects in a repeatable fashion.
I figured out how to clean up the c++ object in the internal field, but I don't have time to put the whole answer here. You have to pass in a Global object into your weak callback by creating a hybrid field (a struct works well) on the heap that has both the global object and a pointer to your c++ object. You can then delete your c++ object, call Reset() on your Global and then delete the whole thing. I'll try to add actual code, but may forget.
Here is a good source: https://code.google.com/p/chromium/codesearch#chromium/src/v8/src/d8.cc&l=1064 lines 1400-1441 are what you want. (edit: line numbers seem to be wrong now - maybe the link above has changed?)
Remember, v8 won't garbage collect small amounts of memory, so you may never see it. Also, just because your program ends doesn't mean the GC will run. You can use isolate->AdjustAmountOfExternalAllocatedMemory(length); to tell v8 about the size of the memory you've allocated (it includes this in its calculations about when there's too much memory in use and GC needs to run) and you can use isolate->IdleNotificationDeadline(1); to give the GC a chance to run (though it may choose not to).
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());
}