Closed nouknouk closed 9 years ago
Hi,
C++ class wrapping with v8pp::class_
is supposed to use for object construction in JavaScript side with new
. And the v8pp::class_<T>::reference_external()
function is rather to make visible in JavaScript an existing C++ object, say some global one.
For simple C++ types like structs I use a way with custom type converter - to specialize v8pp::convert
for the type. The v8pp::convert<T>
static functions is used in v8pp::to_v8()
and v8pp::from_v8()
to convert a C++ type into a corresponding V8 type and vice versa.
For your Coord
it will be like this:
struct Coord
{
int x, y;
};
namespace v8pp {
template<>
struct convert<Coord>
{
using from_type = Coord;
using to_type = v8::Handle<v8::Object>;
static bool is_valid(v8::Isolate*, v8::Handle<v8::Value> value)
{
return value->IsObject();
}
static from_type from_v8(v8::Isolate* isolate, v8::Handle<v8::Value> value)
{
v8::HandleScope scope(isolate);
if (value->IsObject())
{
v8::Local<v8::Object> obj = value->ToObject();
Coord result;
if (get_option(isolate, obj, "x", result.x) && get_option(isolate, obj, "y", result.y))
{
return result;
}
}
throw std::invalid_argument("expected {x, y} object");
}
static v8::Handle<v8::Value> to_v8(v8::Isolate* isolate, Coord const& value)
{
v8::EscapableHandleScope scope(isolate);
v8::Local<v8::Object> obj = v8::Object::New(isolate);
set_option(isolate, obj, "x", value.x);
set_option(isolate, obj, "y", value.y);
return scope.Escape(obj);
}
};
} // namespace v8pp
Hi, and thanks for your reply.
I agree for simple structs. My example was probably too trivial, but I would have other -more complex- types which would be sometimes created on C++ side, sometimes on JS side.
Another example: a (tree organized) Sprite class, with several specialized subclasses (Text, Image, Polygon, ...), with their own properties (text, imageTexture, polygonPoints, other complex objects, ...). Those Sprite instance can contain themselves a list of other sprites (its 'children'), to compose more complex graphical entities, aka "widgets" (let's say a "Button" widget, composed of an Image + a Text + ...).
Especially because of the second point, the Sprite instances must be instanciated from both C++ & JS side.
That's why I was thinking about the usage of smart_ptr, to address the problem of shared ownership between JS & C++ sides: letting my C++ framework use shared_ptr, and adding shared_ptr usage in v8pp. This would avoid an early deletion of an instance whereas the 'other side' still references it, and not forgetting to delete pending unused instances when nobody references them anymore.
What do you think about the basic concept ?
For more complex class hierarchies you may try v8pp::persistent_ptr<T>
template. I used it to store a pointer to C++ object and V8 persistent handle. But C++ object should be wrapped with v8pp::class_
and exposed to JavaScript with v8pp::class_<T>::import_external()
Something like this:
struct Coord
{
int x, y;
};
class Sprite
{
v8pp::persistent_ptr<Coord> pos_;
public:
Coord& position() { return *pos_; }
};
v8pp::class_<Coord> Coord_JS(isolate);
Coord_JS.set("x", &Coord::x).set("y", &Coord::y);
v8pp::class_<Sprite> Sprite_JS(isolate);
Sprite_JS.set("position", v8pp::property(&Sprite::position));
Sprite::Sprite()
{
// create C++ object
Coord* pos = new Coord;
// expose it to JS
v8::Local<v8::Value> js_pos = v8pp::class_<Coord>::import_external(isolate, pos);
// make JS handle persistent
pos_.reset(isolate, js_pos);
}
Probably class_::reference_external
should work too for a pointer to aggregated object. Like this:
struct Coord
{
int x, y;
};
class Sprite
{
Coord pos_;
public:
Coord& position() { return pos_; }
};
v8pp::class_<Coord> Coord_JS(isolate);
Coord_JS.set("x", &Coord::x).set("y", &Coord::y);
v8pp::class_<Sprite> Sprite_JS(isolate);
Sprite_JS.set("position", v8pp::property(&Sprite::position));
Sprite::Sprite()
{
// just reference to pos_ object, it will alive while this Sprite is alive
// expose pos_ to JS
v8pp::class_<Coord>::reference_external(isolate, &pos_);
}
In this case there should be opposite to reference_external()
function, say v8pp::class_<T>::unreference(v8::Isolate*, T* object>
to remove the object from internal list of persistent handles in v8pp::class_
:
Sprite::~Sprite()
{
v8pp::class_<Coord>::unreference(isolate, &pos_);
}
Thanks a lot for your help, I will dig into that.
Hi nouknouk:
i think you can just modify the find_object function. the dangling pointer problem maybe is because the issue in reference_external, it add an external pointer in class_info's object list then delete it, the map.emplace fuction just copy the pointer as first of std::pair.
Hi,
I would have a few questions about properties (JsWrapped) objects nested in other JSWrapped objects. Let's say I have a Sprite c++ class embedding a Coord:
(step 1) I wrap them using the class_ helpers:
(step 2) in my JS script, I instanciate a new Sprite.
Getting the position returns 'undefined'.
I understood it was because the Coord instance was not known from
class_<Coord>::find_object()
, because it has not been instanciated by the v8pp wrappers, rather by my Sprite constructor's class, so on "C++ side"(step 3) I can manage such problem by changing the
find_object()
function, adding kind of 'auto external's referencement':But now, the problem is that when I delete my initial object (the Sprite), (i suppose) the Coord instance will remain indefinitely registered in the
singleton<Coord>
(with a dangling pointer).=> (I'm new to v8pp, so it's probable that) I missed something ?
=> To manage the mapping of the object's lifetimes between "C++ side" and "JS side", should i foresee something the like usage of 'smart pointers' with kind of callback to v8pp when an object is deleted, and also using "smart_ptr aliasing" to recursively delete nested objects (Sprite deleted => delete Coord) ?
Any idea would be highly welcome. Thanks in advance.