Smart pointers and inheritance

213 views
Skip to first unread message

שחר לנגבהיים

unread,
Jul 30, 2019, 8:50:38 AM7/30/19
to emscripten-discuss
Hi,
I have a couple of structs objects, inheriting from a base struct, and passed using smart pointers. I've tried to bind them using embind, but when I look at the object in the debugger I see that it isn't a smart pointer but a raw pointer, and when I try to pass the object to another object, I get an error that the wrong type of object is passed. 

c++ code:
struct RenderModel {};

EMSCRIPTEN_BINDINGS(RenderModelBinding) {
emscripten::class_<RenderModel>("RenderModel")
.constructor<>()
.smart_ptr<std::shared_ptr<RenderModel>>("RenderModel");
}

struct GroupRenderModel : RenderModel {
void addModel(std::shared_ptr<wre_model::RenderModel> model) {
models.push_back(model);
}

std::vector<std::shared_ptr<wre_model::RenderModel>> models;
};

EMSCRIPTEN_BINDINGS(GroupRenderModelBinding) {
emscripten::class_<GroupRenderModel>("GroupRenderModel")
.constructor<>()
.function("addModel", &GroupRenderModel::addModel)
.smart_ptr<std::shared_ptr<GroupRenderModel>>("GroupRenderModel");
}

struct TextureRenderModel : public RenderModel {};

EMSCRIPTEN_BINDINGS(TextureRenderModelBinding) {
emscripten::class_<TextureRenderModel>("TextureRenderModel")
.constructor<>()
.smart_ptr<std::shared_ptr<TextureRenderModel>>("TextureRenderModel");
}

JS code:
const groupLayer = new Module.GroupRenderModel();
const textureLayer = new Module.TextureRenderModel();
groupLayer.addModel(textureLayer);

When checking in the debugger, I see that groupLayer's pointer is:
  1. ptrTypeRegisteredPointer
    1. destructorFunctionnull
    2. isConstfalse
    3. isReferencefalse
    4. isSmartPointerfalse
    5. name"GroupRenderModel*"
textureLayer's pointer is:
  1. ptrTypeRegisteredPointer
    1. destructorFunctionnull
    2. isConstfalse
    3. isReferencefalse
    4. isSmartPointerfalse
    5. name"TextureRenderModel*"
And the error printed when calling setModel is:
  1. BindingError {name: "BindingError", message: "Expected null or instance of RenderModel, got an instance of TextureRenderModel", stack: "BindingError: Expected null or instance of RenderM…ttp://localhost:51840/client.e31bb0bc.js:68387:15"}
  2. message: "Expected null or instance of RenderModel, got an instance of TextureRenderModel"
    1. name"BindingError"
    2. stack"BindingError: Expected null or instance of RenderModel, got an instance of TextureRenderModel↵ at BindingError.<anonymous> (http://localhost:51840/render/appWASM.js:5207:24)↵ at new BindingError (eval at createNamedFunction (http://localhost:51840/render/appWASM.js:1:1), <anonymous>:4:34)↵ at throwBindingError (http://localhost:51840/render/appWASM.js:5225:13)↵ at upcastPointer (http://localhost:51840/render/appWASM.js:5569:15)↵ at RegisteredPointer.genericPointerToWireType [as toWireType] (http://localhost:51840/render/appWASM.js:5622:13)↵ at GroupRenderModel$addModel [as addModel] (eval at new_ (http://localhost:51840/render/appWASM.js:1:1), <anonymous>:9:26)↵ at CompositionCanvas.updateRenderEngine (http://localhost:51840/client.e31bb0bc.js:68401:18)↵ at http://localhost:51840/client.e31bb0bc.js:68387:15"

Using smart_ptr_constructor changes the value in isSmartPointer, but the same binding error is returned.

struct RenderModel {};

EMSCRIPTEN_BINDINGS(RenderModelBinding) {
emscripten::class_<RenderModel>("RenderModel")
.smart_ptr_constructor<std::shared_ptr<RenderModel>>("RenderModel",
&std::make_shared<RenderModel>);
}

struct GroupRenderModel : public RenderModel {
void addModel(std::shared_ptr<wre_model::RenderModel> model) {
models.push_back(model);
}

std::vector<std::shared_ptr<wre_model::RenderModel>> models;
};

EMSCRIPTEN_BINDINGS(GroupRenderModelBinding) {
emscripten::class_<GroupRenderModel>("GroupRenderModel")
.function("addModel", &GroupRenderModel::addModel)
.smart_ptr_constructor<std::shared_ptr<GroupRenderModel>>(
"GroupRenderModel", &std::make_shared<GroupRenderModel>);
}

struct TextureRenderModel : public RenderModel {};

EMSCRIPTEN_BINDINGS(TextureRenderModelBinding) {
emscripten::class_<TextureRenderModel>("TextureRenderModel")
.smart_ptr_constructor<std::shared_ptr<TextureRenderModel>>(
"TextureRenderModel", &std::make_shared<TextureRenderModel>);
}

Am I making some mistake with my binding declaration, or does embind not support implicit casting?


Shachar Langbeheim

unread,
Jul 30, 2019, 9:50:03 AM7/30/19
to emscripten-discuss
I see now that the same thing happens with raw pointers, and is not an issue of smart pointers.

--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/emscripten-discuss/849ef787-772d-4a66-ac98-1d9d3e948b36%40googlegroups.com.

Shachar Langbeheim

unread,
Jul 30, 2019, 10:26:16 AM7/30/19
to emscripten-discuss
OK, so this was me misreading the documentation. Just in case anyone else will be stuck with the same issue, here were my mistakes - 

a. When declaring a derived class, the base class needs to be declared along with the derived class, using class_<Derived, base<Base>>.
b. The reason that the objects returned with smart_ptr_constructor were defined as smart pointers, and the first version wasn't is that the smart_ptr declaration only allows usage of smart pointers, it doesn't automatically force their usage.

Sorry for the noise, I should've RTFM'd twice.

One last thing that I'm not sure about - how do I define smart_ptr_constructor with arguments? 

using this code 
struct TextRenderModel : public RenderModel {
TextRenderModel(float left, float top, float width, float height, const std::string &text,
int size, int xCoord, int yCoord)
: RenderModel(SkRect::MakeXYWH(left, top, width, height)),
text(text),
fontSize(size),
xCoord(xCoord),
yCoord(yCoord) {}

std::string text;

/// Size of the font;
int fontSize;

/// X offset of the drawn text.
int xCoord;

/// Y offset of the drawn text.
int yCoord;
};

EMSCRIPTEN_BINDINGS(TextRenderModelBinding) {
emscripten::class_<TextRenderModel, emscripten::base<TextRenderModel>>("TextRenderModel")
.smart_ptr_constructor<float, float, float, float, std::string, int, int, int>>
("TextRenderModel", &std::make_shared<TextRenderModel>);
}

prints this error:
/render/third_party/emsdk/fastcomp/emscripten/system/include/libcxx/memory:4271:5: error: static_assert failed due to requirement 'is_constructible<TextRenderModel>::value' "Can't construct object in make_shared"
    static_assert( is_constructible<_Tp, _Args...>::value, "Can't construct object in make_shared" );
    ^              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/render/third_party/emsdk/fastcomp/emscripten/system/include/libcxx/memory:4656:29: note: in instantiation of function template specialization 'std::__2::shared_ptr<wre_model::TextRenderModel>::make_shared<>' requested here
    return shared_ptr<_Tp>::make_shared(_VSTD::forward<_Args>(__args)...);
                            ^
./src/model/TextRenderModel.hpp:41:33: note: in instantiation of function template specialization 'std::__2::make_shared<wre_model::TextRenderModel>' requested here
      ("TextRenderModel", &std::make_shared<TextRenderModel>);
                                ^
In file included from <built-in>:1:
In file included from ././src/pch.hpp:4:
/render/third_party/emsdk/fastcomp/emscripten/system/include/libcxx/memory:2089:66: error: no matching constructor for initialization of 'wre_model::TextRenderModel'
  _LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_() {}
 
Reply all
Reply to author
Forward
0 new messages