Discussion: How to avoid manually setting descriptors for each attribute?
edoCsItahW opened this issue · 4 comments
How to avoid manually setting descriptors for each attribute
There is a class that has many properties, but I have a way to dynamically collect these properties, so I use Maps to store them and hope they can be exposed to Nodejs. That's where the problem lies.
Assuming that according to the process, it should be like this
class A {
private:
attr1;
attr2;
...
public:
getAttr1();
setAttr1(attr);
getAttr2();
setAttr2();
Init(env, exports) {
func = DefineClass(env, "A", {
InstanceAccessor("attr1", &A::getAttr1, &A::setAttr1),
InstanceAccessor("attr1", &A::getAttr2, &A::setAttr2),
...
})
}
}
But I don't want to set descriptors for every attribute, so I made the following attempts
- Use decorators
// Definition in napi. h
using InstanceGetterCallback = Napi::Value (T::*)(const CallbackInfo& info);
using InstanceSetterCallback = void (T::*)(const CallbackInfo& info, const Napi::Value& value);
This doesn't work because the T::*
in InstanceGetterCallback
restricts my return type from being a free function
- Retrieve the key from the parameters received by the Setter function
set(const Napi::CallbackInfo& info, const Napi::Value& value)
But info does not contain a key, it contains the same value as value, and the key passed in by the user is unknown what has taken it away
So do you have any good methods?
There's nothing I've found, but if you don't mind doing a bit of debugging & adapting, you can look at: https://github.com/broken/napi-wrapper-gen
It's a small app I wrote that uses antlr to read a c++ header file and generate the corresponding Napi header & source files. It works well for my use-case, and while I've tried to keep it generic, it is not all encompassing and there are likely some app specific things that will need to be adjusted.
Example: from Song.h, it will generate Song_wrap.h & Song_wrap.cpp.
Hi @edoCsItahW ,
But I don't want to set descriptors for every attribute
What is the reasoning behind this, as that would determine a solution.
If it's because you don't want to replicate a lot of "boilerplate" code, then I would suggest taking a look at the solution above.
If it's because you don't want your class prototype to have a bunch of instance methods on it, you could try looking at exposing only one instance method which takes the intended method name + args as its own argument, and use a Proxy
that calls that single instance method.
Hey @broken and @KevinEady,
Thanks a lot for your help!
To start, @KevinEady, you’re right. Maybe I should provide a bit more context. So here’s the deal:
I’m trying to whip up an adapter in C++ to act as middleware between Node and another language (let’s just call it a server for brevity).
When users instantiate a class from the other language in Node and try to access its properties, the best way to handle it is to send the property name to the server, process the returned data into the appropriate type, and then send it back to the user. That’s the root of the issue.
I had already made the call to create a base class and use @broken's tools to add property descriptors—definitely better than doing it for every single class. But, due to some health stuff, I haven’t gotten around to implementing it.
@KevinEady, I’m interested in trying out your approach. I really hadn’t thought about breaking through from the Node side, and it seems promising in theory.
That said, using decorators sounds super tempting, as it could elegantly accomplish a lot with minimal code. So I hope it’s not too forward of me to ask: is there a specific reason for the existence of "T::*"?
This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.