ftk / quickjspp

QuickJS C++ wrapper

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Native inheritance/base classes and multi inheritance

cykoder opened this issue · comments

commented

Hey! Great library.

Is it possible currently to define a native C++ class's base/parent class? Furthermore, is it possible for that definition to include the parents base class and so on. I noticed an in-progress/todo commented function base, I tried to use it and it seemed to work to some extent but there are issues. The prototype properties seem to be there form the parent, but using any of those methods or properties causes an exception "ParentClass object expected"

In essence I would like to do:

        module.class_<Point>("Point")
                .constructor<int, int>()
                .fun<&Point::x>("x")
                .fun<&Point::y>("y")
                .fun<&Point::norm>("norm");

        module.class_<Point3D>("Point3D")
                .base<Point>()
                .constructor<int, int, int>()
                .fun<&Point3D::z>("z")

where

class Point3D : public Point { }

then in JS:

const point = new Point3D(1, 2, 3);
console.log('point xyz', point.x, point.y, point.z);

Echo above on all points :)

  • Great library!
  • I'd also love it if class inheritance was supported in this way
commented

Hello. The problem is that parentclass' methods expect only parentclass classid. To make inheritance work they would need to check if the id is of any of the inherited classes, which might be not very quick in some cases.
Another way is to disable the classid check, which would probably make a program crash if you pass wrong class type from JS, instead of an exception being thrown.
Here's the patch to disable the check:

diff --git a/quickjspp.hpp b/quickjspp.hpp
--- a/quickjspp.hpp	(revision e39f505ee6eedc1e0caae046b9502dfb89daec89)
+++ b/quickjspp.hpp	(date 1629647676903)
@@ -767,7 +767,9 @@
     /// @throws exception if #v doesn't have the correct class id
     static const std::shared_ptr<T>& unwrap(JSContext * ctx, JSValueConst v)
     {
-        auto ptr = reinterpret_cast<std::shared_ptr<T> *>(JS_GetOpaque2(ctx, v, QJSClassId));
+        // auto ptr = reinterpret_cast<std::shared_ptr<T> *>(JS_GetOpaque2(ctx, v, QJSClassId));
+        // disable classid check to make parent methods work
+        auto ptr = reinterpret_cast<std::shared_ptr<T> *>(JS_GetOpaque2(ctx, v, JS_GetClassID(v)));
         if(!ptr)
             throw exception{};
         return *ptr;
@@ -803,8 +805,7 @@
 
     static T * unwrap(JSContext * ctx, JSValueConst v)
     {
-        auto ptr = reinterpret_cast<std::shared_ptr<T> *>(JS_GetOpaque2(ctx, v,
-                                                                        js_traits<std::shared_ptr<T>>::QJSClassId));
+        auto ptr = reinterpret_cast<std::shared_ptr<T> *>(JS_GetOpaque2(ctx, v, JS_GetClassID(v)));
         if(!ptr)
             throw exception{};
         return ptr->get();
@@ -1352,7 +1353,7 @@
                 return *this;
             }
 
-            /* TODO: needs casting to base class
+            /* TODO: needs casting to base class*/
             template <class B>
             class_registrar& base()
             {
@@ -1364,7 +1365,7 @@
                     throw exception{};
                 return *this;
             }
-             */
+
 
             ~class_registrar()
             {

Another way is to keep a flag whether a class is inherited and disable the id check in this case, but it feels kind of hacky.

commented

Yep that class ID check removal seems to have done it, thanks for that! I would argue that any developer should ensure the right type is always passed, but I understand thats not always easy and isnt so user friendly. Perhaps it could be a library configuration option to determine if the class id check should exist for multi-inheritance or not? For my case its game engine scripting so if it'll be slow I'd rather just disable it, for some non-performant use cases/debug mode the extra validation may be useful.

Should I keep the issue open for future reference incase you get around to solving this problem, or shall I close it?

EDIT: On further testing found one thing that isn't working. I have a method push defined like:

void ClassName::push(ClassName* comp);

and I have ChildClassName which inherits ClassName. I can construct it and access its members just fine now, however if I call classNameBase.push(childObject) where classNameBase is of type ClassName and childObject is of type ChildClassName I get an error that it expected a ClassName object. Looks like perhaps another class type/id check somewhere?

Sam, did you solve this one? I'm about to do exactly the same thing (classNameBase.push(childObject)) - also for game engine scripting :)

commented

I didn't unfortunately, although I didn't have much spare time to dive into quickjshpp/quickjs again. @ftk could fixing this be as simple as disabling class ID check like before in another place?

Sam, I found a start toward a possible solution. I'm about to implement this. It's the (current) last major hurdle to getting my project up and running.

commented

Updated the patch in #24 (comment)

Thank ftk. That is perfect. Running like a charm!