my native function should return an object but it returns the called function object. I am using the spidermonkey jsapi to extend my application. I define several classes which are proxies to their c++ counterparts. The usage is like a main object, called 'mx' used to create objects of type 'widget' using a function named 'widget'
Each application object has some kind of ID which can be passed to the js object constructor as an argument. In spidermonkey 1.8.5 i could create a js widget object using the mx widget method
function on_trigger( triggervalue )
{
w = mx.widget("somename");
print_console("mx is " + mx );
print_console("w is " + w );
}
But mx.widget("somename") does not return an object of type widget, it returns the function object:
mx is [object mx]
w is function widget() {
[native code]
}
This is how the jsapi classes and functions are implemented:
JSBool mx_wrapper_getwidget(JSContext* cx, unsigned argc, jsval* argv)
{
JSObject *global = JS_GetGlobalForScopeChain(cx);
jsval v;
JS_GetProperty(cx, global, widget_wrapper_class.name, &v);
JS::CallArgs args = JS::CallArgsFromVp(argc, argv);
JSObject *w = JS_New(cx, JSVAL_TO_OBJECT(v), argc, args.array());
args.rval().setObject( *w );
return JS_TRUE;
}
static JSFunctionSpec mx_wrapper_methods[] =
{
JS_FS("widget", mx_wrapper_getwidget, 1, 0),
JS_FS_END
};
JS_InitClass(cx, global, NULL, &mx_wrapper_class, mx_wrapper_cons, 0, mx_wrapper_properties, mx_wrapper_methods, NULL, NULL)
JS_DefineObject(cx, global, "mx", &mx_wrapper_class, mx_wrapper_proto, 0);
Why does JSBool mx_wrapper_getwidget() return the function object and not the newly created object?
All the best
Heiner
answering myself: among various other minor issues i did not define my class properly
wrong (widget_wrapper_finalize, JSCLASS_NO_OPTIONAL_MEMBERS missing):
static JSClass widget_wrapper_class =
{
MX_JS_WIDGET_CLASSNAME, JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, NULL
};
right:
static JSClass widget_wrapper_class =
{
MX_JS_WIDGET_CLASSNAME, JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, widget_wrapper_finalize, JSCLASS_NO_OPTIONAL_MEMBERS
};
Related
I have a C++ object which inherits from node::ObjectWrap. The object itself manages the resource so it accepts it as an argument in a private constructor.
When object is created from Javascript it's quite straightforward:
void New(const v8::FunctionCallbackInfo<v8::Value>& args) {
// ...
auto handleToCustomResource = resolveFromJSArg(args[0]->ToString());
auto object = new MyObjectWrap(handleToCustomResource);
object->Wrap(args.This());
args.GetReturnValue().Set(args.This());
}
However it's not clear how to wrap the object when it originates from C++, here is my recent effort:
public v8::Local<v8::Object> CreateObject(v8::Isolate *isolate, const char *name) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::EscapableHandleScope scope(isolate);
auto object = new MyObjectWrap(handleToMyCustomResource);
v8::Local<v8::Object> instance = // eh don't have an instance??
object->Wrap(instance);
return scope.Escape(instance);
}
How to properly create a JS object so I could use it as an instance for my native object?
One of workarounds I found is to basically have two constructors, one public and one private and then inherit one from another. The public one is available in javascript, while the private is solely used to create an instance for wrapping an existing ObjectWrap object.
v8::Local<v8::FunctionTemplate> privTpl = v8::FunctionTemplate::New(isolate, PrivateNew);
v8::Local<v8::FunctionTemplate> pubTpl = v8::FunctionTemplate::New(isolate, New);
// define prototype
pubTpl->Inherit(privTpl);
exports->Set(v8::String::NewFromUtf8(isolate, "MyObject"), tpl->GetFunction());
There is a model inherited from QAbstractListModel, I use it in qml. One of the properties of this model are parameters, they are specific to the element type of this model. That is one element the parameters of this class TemperatureParam, DifrentParamType another, a third still something... How can I pass an object to qml and to be sure that the memory is freed after use? The code below now works as I need to, but it seems to me that it's not safe.
Param class is so trivial:
class QuickTemperatureParam : public QObject
{
Q_OBJECT
Q_PROPERTY(float param1 READ param1 WRITE setParam1)
//...
};
Model class (Here's what I'm asking):
class SectionsModel : public QAbstractListModel
{
//...
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override
{
//...
int type = getType( idx );
if (type == 1)
{
auto p = new QuickTemperatureParam( idx );
p->deleteLater(); // This is all right or no?
return qVariantFromValue(p);
}
else if (type == 2)
//...
}
};
QML something like this:
ListView {
model: sectionsModel
delegate: Rectangle {
color: model.statusColor
ItemDelegate {
text: model.title
highlighted: ListView.isCurrentItem
onPressed:
switch ( model.type )
{
case SectionType.Temperature:
temperatureParam.openItem(model)
break;
case SectionType.Lighting:
lightingParam.open(model)
break;
}
}
}
}
Popup {
id: temperatureParam
function openItem(model)
{
var p = model.param
params.itemAt(0).range.from = params.itemAt(1).range.from = p.min
params.itemAt(0).range.to = params.itemAt(1).range.to = p.max
params.itemAt(0).range.setValues( p.dayMin, p.dayMax )
params.itemAt(1).range.setValues( p.nightMin, p.nightMax )
open()
}
}
According to the documentation:
When data is transferred from C++ to QML, the ownership of the data
always remains with C++. The exception to this rule is when a QObject
is returned from an explicit C++ method call: in this case, the QML
engine assumes ownership of the object, unless the ownership of the
object has explicitly been set to remain with C++ by invoking
QQmlEngine::setObjectOwnership() with QQmlEngine::CppOwnership
specified.
Generally an application doesn't need to set an object's ownership explicitly. As you can read here, by default, an object that is created by QML has JavaScriptOwnership.
Objects returned from C++ method calls will be set to JavaScriptOwnership also but this applies only to explicit invocations of Q_INVOKABLE methods or slots.
Because the method data is not an explicit C++ method call, you should consider to set the object ownership to QQmlEngine::JavaScriptOwnership calling setObjectOwnership()
So, in general:
Don't use QQmlEngine::CppOwnership if you want QML to destroy the object. The associated data will be deleted when appropriate (i.e. after the garbage collector has discovered that there are no more live references to the value)
A QSharedPointer probably wouldn't work. You have more information here.
I have a simple Lua script
function TestFunction(Id)
local Factory = TestParent();
local ChildDirect = TestChild("DirectCall");
local ChildFactory1 = Factory:CreateChild("Factory1");
local ChildFactory2 = Factory:CreateChild("Factory2");
result = Ret()
return result
end
that uses two C++ exposed objects (trough luabind)
void TestParent::RegisterToLua(lua_State* lua)
{
// Export our class with LuaBind
luabind::module(lua)
[
luabind::class_<TestParent>("TestParent")
.def(luabind::constructor<>())
.def("CreateChild", &TestParent::CreateChild)
];
}
void TestChild::RegisterToLua(lua_State* lua)
{
// Export our class with LuaBind
luabind::module(lua)
[
luabind::class_<TestChild>("TestChild")
.def(luabind::constructor<std::string>())
.def("GetValue", &TestChild::GetValue)
];
}
I call the function
luabind::object obj = luabind::call_function< luabind::object >(LuaState, "TestFunction", IdParam);
if ( obj.is_valid() )
{
....
}
lua_gc(LuaState, LUA_GCCOLLECT, 0);
During lua_gc call only Factory and ChildDirect objects are destroyed. ChildFactory1 and ChildFactory2 remains allocated. The lua stack remains balanced (has same value - 5 - some tables ) after the luabind::call_function.
What is the problem ? The objects created by Factory remain somehow referenced ? By who ?
CreateChild body is
TestChild* TestParent::CreateChild(std::string strname)
{
return new TestChild(strname);
}
The ownership of the new constructed object should be taken by lua object and destroyed if ChildFactory1 or ChildFactory2 is nil-ed or out of scope.
adopt: Used to transfer ownership across language boundaries.
module(L)
[
def("create", &create, adopt(result))
];
You should return a smart pointer (i.e. a boost::shared_ptr) from your factory.
see: LuaBind Documentation # smart pointer
and a discussion in the LuaBridge docu
I used lualite to wrap the following object in lua:
class SpriteComponent : public Component
{
public:
SpriteComponent();
std::string name() const;
std::string textureId() const;
void setTextureId(const std::string& id);
int spriteCoordX() const;
void setSpriteCoordX(int value);
int spriteCoordY() const;
void setSpriteCoordY(int value);
};
The binding code:
module(L,
class_<SpriteComponent>("SpriteComponent")
.constructor("new")
.def("name", &SpriteComponent::name)
.property("textureId",
&SpriteComponent::textureId, &SpriteComponent::setTextureId)
.property("spriteCoordX",
&SpriteComponent::spriteCoordX, &SpriteComponent::setSpriteCoordX)
.property("spriteCoordY",
&SpriteComponent::spriteCoordY, &SpriteComponent::setSpriteCoordY)
);
Is there a way (either on the lua side or the C++ side) to get a list of properties? If I list the pairs in the resulting table, I only get name and __instance.
local o = SpriteComponent.new()
for key,value in pairs(o) do
print("found member " .. key);
end
I even tried some of these table printers, but no luck.
I'm the author of lualite. I wrote the library to be minimalist and fast and did not foresee the need for reflection :) Anyway, what you are looking for can be found as 2 static members:
static ::std::unordered_map<char const*, detail::map_member_info_type,
detail::unordered_hash, detail::unordered_eq> getters_;
static ::std::unordered_map<char const*, detail::map_member_info_type,
detail::unordered_hash, detail::unordered_eq> setters_;
the char const* is the name of the property, the value being a map_member_info_type, which are essentially two pointers, one to a lualite stub, the other to the C++ member function.
struct map_member_info_type
{
lua_CFunction callback;
member_func_type func;
};
If you like, I can make both members public. The way properties work is as follows:
A default getter is (usually) set in the wrapped class's instance table:
lua_pushcclosure(L, default_getter<C>, 2);
rawsetfield(L, -2, "__index");
This points to the default getter:
template <class C>
int default_getter(lua_State* const L)
{
assert(2 == lua_gettop(L));
auto const i(lualite::class_<C>::getters_.find(lua_tostring(L, 2)));
return lualite::class_<C>::getters_.end() == i
? 0
: (lua_pushlightuserdata(L, &i->second.func),
lua_replace(L, lua_upvalueindex(2)),
(i->second.callback)(L));
}
which looks for the name of the property (which is on the stack). That could be anything really and if it does not find the name, it returns 0, otherwise, it forwards the call to the lualite stub made for the member function, which then handles the arguments and the return value(s).
If you could tolerate having to list the property names in your Lua code, this could be a solution:
local GetPropertyList
do -- to make `property_names` "private" to GetPropertyList
property_names = {
["SpriteComponent"] = { "textureId", "spriteCoordX", "spriteCoordY" },
-- property names for other components, e.g.
["AnotherComponentName"] = { "propName1", "propName2" },
}
function GetPropertyList( object ) --[[local]]
local component_name = object:name()
local prop_names = property_names[component_name]
if not prop_names then
error( "unsupported component" )
end
local res = {}
for _, p_name in ipairs( prop_names ) do
local p_val = object[p_name]
res[ #res + 1 ] = p_val
-- or use this if you want a key-value map:
-- res[p_name] = p_val
end
return res
end -- function
end -- do
for k, v in pairs( GetPropertyList(o) ) do
print( k, v )
end
I'm writing Agueas [1] addon for Node.js
For now I have synchronous code, C++ class looks like this:
class LibAugeas : public node::ObjectWrap {
public:
static void Init(Handle<Object> target);
protected:
augeas * m_aug;
LibAugeas();
~LibAugeas();
static Handle<Value> New(const Arguments& args);
static Handle<Value> get (const Arguments& args);
static Handle<Value> set (const Arguments& args);
static Handle<Value> setm (const Arguments& args);
// other methods
};
Usage of this class in JS:
var lib = require('...');
var aug = new lib.Augeas(...);
aug.set(...);
aug.get(...);
// etc
I'm going to impelement asynchronous code.
The bottleneck is creating augeas object (aug_init) while all or some lenses and files are being loaded and parsed. So the idea is creating augeas object asynchronously, and then pass created JS object in a callback function:
Pure C thread: call aug_init(), aug_load() to get augeas handle.
When ready, use augeas handle to creat JS object (see the first snippet)
Pass created JS object to callback function.
Usage might be as such:
lib.heracles(function(aug) {
if (!aug.error()) {
console.log('Hello!');
// async save:
aug.save(function(err, msg) {
console.log(msg);
});
} else {
console.log('Sad, but true :-(');
}
}
);
And finally, my problem: I do not know how to create JS object in C++ :-)
Constructor static Handle<Value> New(const Arguments& args); returns args.This(), but when I'm in C++ code I do not have args and also can't wrap object.
So, how do I create JS object in C++? Please, don't break my heart saying it is not possible :-)
[1] http://augeas.net
Ok, thanks to everyone :-)
I've found the right way. Here is a static method which creates an JS object wrapping given augeas handle.
Then I can pass this object to callback function from C++ code.
Local<Object> LibAugeas::New(augeas *aug)
{
LibAugeas *obj = new LibAugeas();
obj->m_aug = aug;
Handle<ObjectTemplate> tpl = ObjectTemplate::New();
tpl->SetInternalFieldCount(1); // one field for LibAugeas* pointer (via obj->Wrap())
#define _OBJ_NEW_METHOD(m) NODE_SET_METHOD(tpl, #m, m)
_OBJ_NEW_METHOD(get);
_OBJ_NEW_METHOD(set);
_OBJ_NEW_METHOD(setm);
_OBJ_NEW_METHOD(rm);
_OBJ_NEW_METHOD(mv);
_OBJ_NEW_METHOD(save);
_OBJ_NEW_METHOD(nmatch);
_OBJ_NEW_METHOD(insert);
_OBJ_NEW_METHOD(error);
Local<Object> O = tpl->NewInstance();
obj->Wrap(O);
return O;
}