diff --git a/scripts/mkqtdecl6/mkqtdecl.conf b/scripts/mkqtdecl6/mkqtdecl.conf index 608c946e5..93dfe634b 100644 --- a/scripts/mkqtdecl6/mkqtdecl.conf +++ b/scripts/mkqtdecl6/mkqtdecl.conf @@ -807,6 +807,13 @@ include "QVector4D", [ "", "", "" ] include "QAction", [ "", "", "", "" ] include "QCursor", [ "", "", "" ] include "QGraphicsItem", [ "", "", "", "", "", "", "", "", "", "", "", "" ] +include "QGraphicsObject", [ "", "", "", "", "", "", + "", "", "", "", "", + "", "", "", "", + "", "", "", "", "", "", "", + "", "", "", "", "", "", + "", "", "", + "" ] include "QGraphicsScene", [ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" ] include "QGuiApplication", [ "", "", "", "", "", "", "", "" ] include "QApplication", [ "", "", "", "", "", "", "", "" ] diff --git a/scripts/mkqtdecl_common/produce.rb b/scripts/mkqtdecl_common/produce.rb index 56484d2b3..dfbc077f6 100755 --- a/scripts/mkqtdecl_common/produce.rb +++ b/scripts/mkqtdecl_common/produce.rb @@ -265,7 +265,7 @@ class CPPStruct cls = self.id.to_s - # collect enums from inner classes + # collect used enums from inner classes (self.body_decl || []).each do |bd| decl = nil if bd.is_a?(CPPStructDeclaration) && bd.visibility == :public && bd.struct.body_decl && bd.myself != "" && !conf.is_class_dropped?(cls, bd.myself) @@ -273,6 +273,14 @@ class CPPStruct end end + # collect used enums from base classes + (self.base_classes || []).each do |bc| + bc_obj = self.parent.resolve_qid(bc.class_id) + if bc_obj.is_a?(CPPStructDeclaration) && bc_obj.visibility == :public && bc_obj.struct.body_decl && bc_obj.myself != "" && !conf.is_class_dropped?(cls, bc_obj.myself) + bc_obj.struct.collect_used_enums(map, conf) + end + end + methods = {} self.collect_all_methods(methods, conf) @@ -315,7 +323,7 @@ class CPPStruct end - def collect_methods(map) + def collect_methods(map, weak = false) mmap = {} @@ -355,6 +363,7 @@ class CPPStruct end # take non-duplicates (by call signature) for the map + # weak ones do not redefine methods mmap.each do |mn,decls| seen = {} @@ -362,7 +371,9 @@ class CPPStruct s = d.call_sig if !seen[s] seen[s] = true - (map[mn] ||= []) << d + if !weak || !map[mn] + (map[mn] ||= []) << d + end end end @@ -1907,6 +1918,10 @@ END base_cls = base_classes[0] && base_classes[0].class_id.to_s base_clsn = base_cls && make_cls_name(base_cls) + # as we only support single base classes (a tribute to Ruby), we treat all other base classes as + # mixins + mixin_base_classes = base_classes[1..] || [] + methods_by_name = {} all_methods_by_name = {} enum_decls_by_name = {} @@ -1918,8 +1933,8 @@ END end mmap = {} - struct.collect_methods(methods_by_name) struct.collect_all_methods(all_methods_by_name, conf) + struct.collect_methods(methods_by_name) struct.collect_enum_decls(enum_decls_by_name) { |bd| self.is_enum_used?(bd) || conf.is_enum_included?(cls, bd.myself) } # if one method is abstract, omit ctors for example @@ -2365,35 +2380,35 @@ END base_classes.each do |bc| - bc_name = bc.class_id.to_s + bc_name = bc.class_id.to_s - ofile.puts("// base class cast for #{bc_name}") - ofile.puts("") - ofile.puts("static void _init_f_#{clsn}_as_#{bc_name} (qt_gsi::GenericMethod *decl)") - ofile.puts("{") - ofile.puts(" decl->set_return<#{bc_name} *> ();") - ofile.puts("}") - ofile.puts("") - ofile.puts("static void _call_f_#{clsn}_as_#{bc_name} (const qt_gsi::GenericMethod *, void *cls, gsi::SerialArgs &, gsi::SerialArgs &ret) ") - ofile.puts("{") - ofile.puts(" ret.write<#{bc_name} *> ((#{bc_name} *)(#{cls} *)cls);") - ofile.puts("}") - ofile.puts("") + ofile.puts("// base class cast for #{bc_name}") + ofile.puts("") + ofile.puts("static void _init_f_#{clsn}_as_#{bc_name} (qt_gsi::GenericMethod *decl)") + ofile.puts("{") + ofile.puts(" decl->set_return<#{bc_name} *> ();") + ofile.puts("}") + ofile.puts("") + ofile.puts("static void _call_f_#{clsn}_as_#{bc_name} (const qt_gsi::GenericMethod *, void *cls, gsi::SerialArgs &, gsi::SerialArgs &ret) ") + ofile.puts("{") + ofile.puts(" ret.write<#{bc_name} *> ((#{bc_name} *)(#{cls} *)cls);") + ofile.puts("}") + ofile.puts("") - mdecl_bcc << "new qt_gsi::GenericMethod (\"as#{bc_name}\", \"@brief Delivers the base class interface #{bc_name} of #{cls}\\nClass #{cls} is derived from multiple base classes. This method delivers the #{bc_name} base class aspect.\", false, &_init_f_#{clsn}_as_#{bc_name}, &_call_f_#{clsn}_as_#{bc_name});" + mdecl_bcc << "new qt_gsi::GenericMethod (\"as#{bc_name}\", \"@brief Delivers the base class interface #{bc_name} of #{cls}\\nClass #{cls} is derived from multiple base classes. This method delivers the #{bc_name} base class aspect.\", false, &_init_f_#{clsn}_as_#{bc_name}, &_call_f_#{clsn}_as_#{bc_name});" - ofile.puts("static void _init_f_#{clsn}_as_const_#{bc_name} (qt_gsi::GenericMethod *decl)") - ofile.puts("{") - ofile.puts(" decl->set_return ();") - ofile.puts("}") - ofile.puts("") - ofile.puts("static void _call_f_#{clsn}_as_const_#{bc_name} (const qt_gsi::GenericMethod *, void *cls, gsi::SerialArgs &, gsi::SerialArgs &ret) ") - ofile.puts("{") - ofile.puts(" ret.write ((const #{bc_name} *)(const #{cls} *)cls);") - ofile.puts("}") - ofile.puts("") + ofile.puts("static void _init_f_#{clsn}_as_const_#{bc_name} (qt_gsi::GenericMethod *decl)") + ofile.puts("{") + ofile.puts(" decl->set_return ();") + ofile.puts("}") + ofile.puts("") + ofile.puts("static void _call_f_#{clsn}_as_const_#{bc_name} (const qt_gsi::GenericMethod *, void *cls, gsi::SerialArgs &, gsi::SerialArgs &ret) ") + ofile.puts("{") + ofile.puts(" ret.write ((const #{bc_name} *)(const #{cls} *)cls);") + ofile.puts("}") + ofile.puts("") - mdecl_bcc << "new qt_gsi::GenericMethod (\"asConst#{bc_name}\", \"@brief Delivers the base class interface #{bc_name} of #{cls}\\nClass #{cls} is derived from multiple base classes. This method delivers the #{bc_name} base class aspect.\\n\\nUse this version if you have a const reference.\", true, &_init_f_#{clsn}_as_const_#{bc_name}, &_call_f_#{clsn}_as_const_#{bc_name});" + mdecl_bcc << "new qt_gsi::GenericMethod (\"asConst#{bc_name}\", \"@brief Delivers the base class interface #{bc_name} of #{cls}\\nClass #{cls} is derived from multiple base classes. This method delivers the #{bc_name} base class aspect.\\n\\nUse this version if you have a const reference.\", true, &_init_f_#{clsn}_as_const_#{bc_name}, &_call_f_#{clsn}_as_const_#{bc_name});" end @@ -2474,6 +2489,23 @@ END end + # Produce the mixin base classes + + if ! mixin_base_classes.empty? + ofile.puts("// Additional base classes") + ofile.puts("") + mixin_base_classes.each do |bc| + bc_name = bc.class_id.to_s + ofile.puts("gsi::Class<#{bc_name}> &qtdecl_#{bc_name} ();") + end + ofile.puts("") + end + + mixin_base_classes.each do |bc| + bc_name = bc.class_id.to_s + ofile.puts("gsi::ClassExt<#{cls}> base_class_#{bc_name}_in_#{clsn} (qtdecl_#{bc_name} ());") + end + ofile.puts("") ofile.puts("GSI_#{modn.upcase}_PUBLIC gsi::Class<#{cls}> &qtdecl_#{clsn} () { return decl_#{clsn}; }") ofile.puts("") @@ -3213,7 +3245,7 @@ bp.read(input_file) puts("Collecting used enums ..") l = bp.prod_list(conf) l.each_with_index do |decl_obj,i| - puts "#{decl_obj.myself}: #{i+1}/#{l.size}" + decl_obj.myself && puts("#{decl_obj.myself}: #{i+1}/#{l.size}") bp.collect_used_enums(conf, decl_obj) end diff --git a/scripts/mkqtdecl_common/reader_ext.rb b/scripts/mkqtdecl_common/reader_ext.rb index fcc225888..985f86f06 100644 --- a/scripts/mkqtdecl_common/reader_ext.rb +++ b/scripts/mkqtdecl_common/reader_ext.rb @@ -196,7 +196,7 @@ module QualifiedNameResolver @id2obj && @id2obj[id] end - def resolve_qid(qid) + def resolve_qid(qid, stop = nil, include_other = true) qid.is_a?(CPPQualifiedId) || raise("Argument of resolve_qid must be a CPPQualifiedId object") @@ -206,22 +206,30 @@ module QualifiedNameResolver while root.parent root = root.parent end - obj = root.resolve_qid(qid) + obj = root.resolve_qid(qid, nil, false) else obj = id2obj(qid.parts[0].id) if obj && qid.parts.size > 1 # The part may be a typedef: resolve it in that case before we proceed while obj && obj.is_a?(CPPTypedef) - obj = obj.type.concrete.is_a?(CPPQualifiedId) && self.resolve_qid(obj.type.concrete) + obj = obj.type.concrete.is_a?(CPPQualifiedId) && self.resolve_qid(obj.type.concrete, stop, include_other) end if obj qid_new = qid.dup qid_new.parts = qid.parts[1 .. -1] - obj = obj.respond_to?(:resolve_qid) && obj.resolve_qid(qid_new) + obj = obj.respond_to?(:resolve_qid) && obj.resolve_qid(qid_new, stop, include_other) end end - if ! obj && self.parent - obj = self.parent.resolve_qid(qid) + if ! obj && include_other + # try base classes + self.other_children.each do |bc| + if bc != self && bc.respond_to?(:resolve_qid) + (obj = bc.resolve_qid(qid, self, false)) && break + end + end + end + if ! obj && self.parent && self.parent != stop + obj = self.parent.resolve_qid(qid, stop, include_other) end end @@ -281,6 +289,10 @@ class CPPDeclaration [] end + def other_children + [] + end + def myself self.type.name end @@ -295,6 +307,10 @@ class CPPEnumDeclaration [] end + def other_children + [] + end + def myself # exclude forward declarations self.enum.specs && self.enum.name.to_s @@ -310,6 +326,10 @@ class CPPEnumSpec [] end + def other_children + [] + end + def myself self.name.to_s end @@ -353,6 +373,10 @@ class CPPTypedef [] end + def other_children + [] + end + end class CPPStructDeclaration @@ -395,7 +419,7 @@ class CPPStructDeclaration # The parent may be null for template base classes which are # forward-declared .. we're not interested in this case. if self.parent - bc_obj = self.parent.resolve_qid(bc.class_id) + bc_obj = self.parent.resolve_qid(bc.class_id, nil, false) # NOTE: it may look strange to test whether the base class is the class itself but # since we do a half-hearted job of resolving template variants, this may happen # if we derive a template specialization from another one (specifically @@ -524,6 +548,10 @@ class CPPModule end + def other_children + [] + end + def remove(d) self.decls.delete(d) end diff --git a/src/gsi/gsi/gsiClass.h b/src/gsi/gsi/gsiClass.h index 48ec4e0ca..f5fe0dbf2 100644 --- a/src/gsi/gsi/gsiClass.h +++ b/src/gsi/gsi/gsiClass.h @@ -316,7 +316,7 @@ public: * This feature is not quite useful usually and is reserved for special use cases * such as including enums into a declaration namespace. */ - ClassExt (const ClassBase &import, const std::string &name, const std::string &doc = std::string ()) + ClassExt (const ClassBase &import, const std::string &name = std::string (), const std::string &doc = std::string ()) : ClassBase (doc, Methods ()), mp_declaration (&import) { set_name (name); diff --git a/src/gsi/gsi_test/gsiTest.cc b/src/gsi/gsi_test/gsiTest.cc index 68925c36d..fac4eadd9 100644 --- a/src/gsi/gsi_test/gsiTest.cc +++ b/src/gsi/gsi_test/gsiTest.cc @@ -1664,5 +1664,32 @@ static gsi::Class decl_gfactory (decl_gfactory_base, "", "GFactory", gsi::factory_callback ("f", &GFactory_P::f, &GFactory_P::f_cb) ); +static gsi::Class decl_b1 ("", "B1", + gsi::method ("get1", &B1::get1) + + gsi::method ("set1", &B1::set1) + + gsi::constant ("C1", 42) +); + +static gsi::Class decl_b2 ("", "B2", + gsi::constant ("C2", 17) +); + +static gsi::Class decl_b3 ("", "B3", + gsi::constant ("C3", -1) +); + +gsi::EnumIn enum_in_b3 ("", "E", + gsi::enum_const ("E3A", B3::E3A) + + gsi::enum_const ("E3B", B3::E3B) + + gsi::enum_const ("E3C", B3::E3C) +); + +// 3 base classes +static gsi::Class decl_bb (decl_b1, "", "BB", + gsi::method ("d3", &BB::d3) +); +gsi::ClassExt b2_in_bb (decl_b2); +gsi::ClassExt b3_in_bb (decl_b3); + } diff --git a/src/gsi/gsi_test/gsiTest.h b/src/gsi/gsi_test/gsiTest.h index c41933909..b4e66f206 100644 --- a/src/gsi/gsi_test/gsiTest.h +++ b/src/gsi/gsi_test/gsiTest.h @@ -1397,6 +1397,37 @@ private: int m_tag; }; +class B1 +{ +public: + B1 () : m_value (0) {} + + int get1 () { return m_value; } + void set1 (int v) { m_value = v; } +private: + int m_value; +}; + +class B2 +{ +public: + B2 () {} +}; + +class B3 +{ +public: + B3 () {} + enum E { E3A = 100, E3B = 101, E3C = 102 }; +}; + +class BB + : public B1, public B2, public B3 +{ +public: + int d3 (B3::E a, B3::E b) { return b - a; } +}; + } #endif diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc b/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc index 3b83e14d9..ebd882299 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc @@ -846,6 +846,11 @@ qt_gsi::QtNativeClass decl_QIODevice (qtdecl_QObject (), "QtCore", "Q methods_QIODevice (), "@qt\n@brief Binding of QIODevice"); +// Additional base classes + +gsi::Class &qtdecl_QIODeviceBase (); + +gsi::ClassExt base_class_QIODeviceBase_in_QIODevice (qtdecl_QIODeviceBase ()); GSI_QTCORE_PUBLIC gsi::Class &qtdecl_QIODevice () { return decl_QIODevice; } diff --git a/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc b/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc index fc7eba35c..e87d211bd 100644 --- a/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc +++ b/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc @@ -776,6 +776,19 @@ gsi::Class &qtdecl_QXmlContentHandler (); gsi::Class decl_QXmlDefaultHandler (qtdecl_QXmlContentHandler (), "QtCore5Compat", "QXmlDefaultHandler_Native", methods_QXmlDefaultHandler (), "@hide\n@alias QXmlDefaultHandler"); +// Additional base classes + +gsi::Class &qtdecl_QXmlErrorHandler (); +gsi::Class &qtdecl_QXmlDTDHandler (); +gsi::Class &qtdecl_QXmlEntityResolver (); +gsi::Class &qtdecl_QXmlLexicalHandler (); +gsi::Class &qtdecl_QXmlDeclHandler (); + +gsi::ClassExt base_class_QXmlErrorHandler_in_QXmlDefaultHandler (qtdecl_QXmlErrorHandler ()); +gsi::ClassExt base_class_QXmlDTDHandler_in_QXmlDefaultHandler (qtdecl_QXmlDTDHandler ()); +gsi::ClassExt base_class_QXmlEntityResolver_in_QXmlDefaultHandler (qtdecl_QXmlEntityResolver ()); +gsi::ClassExt base_class_QXmlLexicalHandler_in_QXmlDefaultHandler (qtdecl_QXmlLexicalHandler ()); +gsi::ClassExt base_class_QXmlDeclHandler_in_QXmlDefaultHandler (qtdecl_QXmlDeclHandler ()); GSI_QTCORE5COMPAT_PUBLIC gsi::Class &qtdecl_QXmlDefaultHandler () { return decl_QXmlDefaultHandler; } diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc b/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc index ba86e7774..7817757a1 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc @@ -313,6 +313,11 @@ gsi::Class &qtdecl_QObject (); qt_gsi::QtNativeClass decl_QOffscreenSurface (qtdecl_QObject (), "QtGui", "QOffscreenSurface_Native", methods_QOffscreenSurface (), "@hide\n@alias QOffscreenSurface"); +// Additional base classes + +gsi::Class &qtdecl_QSurface (); + +gsi::ClassExt base_class_QSurface_in_QOffscreenSurface (qtdecl_QSurface ()); GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QOffscreenSurface () { return decl_QOffscreenSurface; } diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc b/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc index 5e3d6df43..756ed7718 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc @@ -297,6 +297,11 @@ gsi::Class &qtdecl_QWindow (); qt_gsi::QtNativeClass decl_QPaintDeviceWindow (qtdecl_QWindow (), "QtGui", "QPaintDeviceWindow_Native", methods_QPaintDeviceWindow (), "@hide\n@alias QPaintDeviceWindow"); +// Additional base classes + +gsi::Class &qtdecl_QPaintDevice (); + +gsi::ClassExt base_class_QPaintDevice_in_QPaintDeviceWindow (qtdecl_QPaintDevice ()); GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QPaintDeviceWindow () { return decl_QPaintDeviceWindow; } diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc b/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc index 6b134be11..0129c0ab5 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc @@ -382,6 +382,11 @@ gsi::Class &qtdecl_QObject (); qt_gsi::QtNativeClass decl_QPdfWriter (qtdecl_QObject (), "QtGui", "QPdfWriter_Native", methods_QPdfWriter (), "@hide\n@alias QPdfWriter"); +// Additional base classes + +gsi::Class &qtdecl_QPagedPaintDevice (); + +gsi::ClassExt base_class_QPagedPaintDevice_in_QPdfWriter (qtdecl_QPagedPaintDevice ()); GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QPdfWriter () { return decl_QPdfWriter; } diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc b/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc index dd8faab17..f654a43b0 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc @@ -2206,6 +2206,11 @@ gsi::Class &qtdecl_QObject (); qt_gsi::QtNativeClass decl_QWindow (qtdecl_QObject (), "QtGui", "QWindow_Native", methods_QWindow (), "@hide\n@alias QWindow"); +// Additional base classes + +gsi::Class &qtdecl_QSurface (); + +gsi::ClassExt base_class_QSurface_in_QWindow (qtdecl_QSurface ()); GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QWindow () { return decl_QWindow; } diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc index 134294b90..e37dc80c4 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc @@ -450,6 +450,11 @@ gsi::Class decl_QAccessibleWidget (qtdecl_QAccessibleObject ( methods_QAccessibleWidget (), "@qt\n@brief Binding of QAccessibleWidget"); +// Additional base classes + +gsi::Class &qtdecl_QAccessibleActionInterface (); + +gsi::ClassExt base_class_QAccessibleActionInterface_in_QAccessibleWidget (qtdecl_QAccessibleActionInterface ()); GSI_QTWIDGETS_PUBLIC gsi::Class &qtdecl_QAccessibleWidget () { return decl_QAccessibleWidget; } diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc index adb8b93a6..87235928a 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc @@ -58,6 +58,7 @@ #include #include #include +#include #include "gsiQt.h" #include "gsiQtWidgetsCommon.h" #include @@ -242,6 +243,11 @@ gsi::Class &qtdecl_QObject (); qt_gsi::QtNativeClass decl_QGraphicsObject (qtdecl_QObject (), "QtWidgets", "QGraphicsObject_Native", methods_QGraphicsObject (), "@hide\n@alias QGraphicsObject"); +// Additional base classes + +gsi::Class &qtdecl_QGraphicsItem (); + +gsi::ClassExt base_class_QGraphicsItem_in_QGraphicsObject (qtdecl_QGraphicsItem ()); GSI_QTWIDGETS_PUBLIC gsi::Class &qtdecl_QGraphicsObject () { return decl_QGraphicsObject; } diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc index ecc208895..fc008a543 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc @@ -1374,6 +1374,11 @@ gsi::Class &qtdecl_QGraphicsObject (); qt_gsi::QtNativeClass decl_QGraphicsWidget (qtdecl_QGraphicsObject (), "QtWidgets", "QGraphicsWidget_Native", methods_QGraphicsWidget (), "@hide\n@alias QGraphicsWidget"); +// Additional base classes + +gsi::Class &qtdecl_QGraphicsLayoutItem (); + +gsi::ClassExt base_class_QGraphicsLayoutItem_in_QGraphicsWidget (qtdecl_QGraphicsLayoutItem ()); GSI_QTWIDGETS_PUBLIC gsi::Class &qtdecl_QGraphicsWidget () { return decl_QGraphicsWidget; } diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc index 0761305dd..59c675d3a 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc @@ -1013,6 +1013,11 @@ gsi::Class &qtdecl_QObject (); qt_gsi::QtNativeClass decl_QLayout (qtdecl_QObject (), "QtWidgets", "QLayout_Native", methods_QLayout (), "@hide\n@alias QLayout"); +// Additional base classes + +gsi::Class &qtdecl_QLayoutItem (); + +gsi::ClassExt base_class_QLayoutItem_in_QLayout (qtdecl_QLayoutItem ()); GSI_QTWIDGETS_PUBLIC gsi::Class &qtdecl_QLayout () { return decl_QLayout; } diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc index 5572da466..dd739fa4b 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc @@ -4981,6 +4981,11 @@ gsi::Class &qtdecl_QObject (); qt_gsi::QtNativeClass decl_QWidget (qtdecl_QObject (), "QtWidgets", "QWidget_Native", methods_QWidget (), "@hide\n@alias QWidget"); +// Additional base classes + +gsi::Class &qtdecl_QPaintDevice (); + +gsi::ClassExt base_class_QPaintDevice_in_QWidget (qtdecl_QPaintDevice ()); GSI_QTWIDGETS_PUBLIC gsi::Class &qtdecl_QWidget () { return decl_QWidget; } diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index acaab2780..1289e2a1c 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -2384,6 +2384,10 @@ PythonModule::make_classes (const char *mod_name) PYASignal::make_class (module); std::list sorted_classes = gsi::ClassBase::classes_in_definition_order (mod_name); + + // ... @@@ find all extensions for a class ... + + // ... @@@ reverse this order ... for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { if (mod_name && (*c)->module () != mod_name) { @@ -2398,6 +2402,7 @@ PythonModule::make_classes (const char *mod_name) // duplication of enums into child classes). In this case we create a constant inside the // target class. if ((*c)->declaration () != *c) { + // ... @@@ continue if named ... tl_assert ((*c)->parent () != 0); // top-level classes should be merged PyTypeObject *parent_type = PythonClassClientData::py_type (*(*c)->parent ()->declaration ()); PyTypeObject *type = PythonClassClientData::py_type (*(*c)->declaration ()); @@ -2414,6 +2419,7 @@ PythonModule::make_classes (const char *mod_name) m_classes.push_back (*c); PythonRef bases; + // ... @@@ collect bases (from this base and extensions) ... if ((*c)->base () != 0) { bases = PythonRef (PyTuple_New (1)); PyTypeObject *pt = PythonClassClientData::py_type (*(*c)->base ()); @@ -2465,6 +2471,8 @@ PythonModule::make_classes (const char *mod_name) PyModule_AddObject (module, (*c)->name ().c_str (), (PyObject *) type); } + // ... @@@ add the named extensions as attributes (see above) ... + // Build the attributes now ... MethodTable *mt = MethodTable::method_table_by_class (*c); diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 7aaa7899e..aafe5e881 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1111,7 +1111,7 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor) } - } catch (tl::CancelException) { + } catch (tl::CancelException &) { // break encountered } @@ -1521,52 +1521,112 @@ rba_add_path (const std::string &path) } } -static void -rba_init (RubyInterpreterPrivateData *d) +namespace { - VALUE module = rb_define_module ("RBA"); - // initialize the locked object vault as a fast replacement for rb_gc_register_address/rb_gc_unregister_address. - rba::make_locked_object_vault (module); +class RubyClassGenerator +{ +public: + RubyClassGenerator (VALUE module) + : m_module (module) + { + // .. nothing yet .. + } - // save all constants for later (we cannot declare them while we are still producing classes - // because of the enum representative classes and enum constants are important) - std::vector constants; + // needs to be called before for each extension before the classes are made + void register_extension (const gsi::ClassBase *cls) + { + if (cls->name ().empty ()) { - std::list sorted_classes = gsi::ClassBase::classes_in_definition_order (); - for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + // got an extension + tl_assert (cls->parent ()); + m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); + m_extensions.insert (cls->declaration ()); - // we might encounter a child class which is a reference to a top-level class (e.g. - // duplication of enums into child classes). In this case we create a constant inside the - // target class. - if ((*c)->declaration () != *c) { - tl_assert ((*c)->parent () != 0); // top-level classes should be merged - rb_define_const (ruby_cls ((*c)->parent ()->declaration ()), (*c)->name ().c_str (), ruby_cls ((*c)->declaration ())); - continue; + } else { + + m_links_for [cls->parent ()->declaration ()].push_back (std::make_pair (cls->name (), cls->declaration ())); + + } + } + + VALUE make_class (const gsi::ClassBase *cls) + { + if (is_registered (cls)) { + return ruby_cls (cls); } + bool is_extension = (m_extensions.find (cls) != m_extensions.end ()); + + bool base_class_is_extension = (m_extensions.find (cls->base ()) != m_extensions.end ()); + VALUE super = rb_cObject; - if ((*c)->base () != 0) { - tl_assert (is_registered ((*c)->base ())); - super = ruby_cls ((*c)->base ()); + if (cls->base () != 0) { + super = make_class (cls->base ()); } VALUE klass; - if ((*c)->parent ()) { - tl_assert (is_registered ((*c)->parent ()->declaration ())); - VALUE parent_class = ruby_cls ((*c)->parent ()->declaration ()); - klass = rb_define_class_under (parent_class, (*c)->name ().c_str (), super); + if (is_extension) { + + if (tl::verbosity () >= 20) { + tl::log << tl::to_string (tr ("Registering class as Ruby module:) ")) << cls->name (); + } + if (cls->base () && ! base_class_is_extension) { + tl::warn << tl::to_string (tr ("Base class of mixin module ignored: ")) << cls->name (); + } + + if (cls->parent ()) { + VALUE parent_class = make_class (cls->parent ()->declaration ()); + klass = rb_define_module_under (parent_class, cls->name ().c_str ()); + } else { + klass = rb_define_module_under (m_module, cls->name ().c_str ()); + } + } else { - klass = rb_define_class_under (module, (*c)->name ().c_str (), super); + + VALUE direct_super = base_class_is_extension ? rb_cObject : super; + if (cls->parent ()) { + VALUE parent_class = make_class (cls->parent ()->declaration ()); + klass = rb_define_class_under (parent_class, cls->name ().c_str (), direct_super); + } else { + klass = rb_define_class_under (m_module, cls->name ().c_str (), direct_super); + } + + rb_define_alloc_func (klass, alloc_proxy); + } - register_class (klass, *c); + // if the base class is an extension (mixin), we cannot use it as superclass because it's a module + if (cls->base () != 0 && base_class_is_extension) { + rb_include_module (klass, super); + } - rb_define_alloc_func (klass, alloc_proxy); + register_class (klass, cls); - MethodTable *mt = MethodTable::method_table_by_class (*c, true /*force init*/); + // mix-in unnamed extensions - for (gsi::ClassBase::method_iterator m = (*c)->begin_methods (); m != (*c)->end_methods (); ++m) { + auto exts = m_extensions_for.find (cls); + if (exts != m_extensions_for.end ()) { + for (auto ie = exts->second.begin (); ie != exts->second.end (); ++ie) { + VALUE ext_module = make_class (*ie); + rb_include_module (klass, ext_module); + rb_extend_object (klass, ext_module); + } + } + + // add named extensions + + auto links = m_links_for.find (cls); + if (links != m_links_for.end ()) { + for (auto il = links->second.begin (); il != links->second.end (); ++il) { + VALUE linked_class = make_class (il->second); + rb_define_const (klass, il->first.c_str (), linked_class); + } + } + + MethodTable *mt = MethodTable::method_table_by_class (cls, true /*force init*/); + + for (auto m = (cls)->begin_methods (); m != (cls)->end_methods (); ++m) { if (! (*m)->is_callback ()) { @@ -1582,7 +1642,7 @@ rba_init (RubyInterpreterPrivateData *d) if (! drop_method) { - for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { + for (auto syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { if (syn->is_predicate) { mt->add_method (syn->name, *m); mt->add_method (syn->name + "?", *m); @@ -1597,17 +1657,17 @@ rba_init (RubyInterpreterPrivateData *d) } else { - for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { + for (auto syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { if (isupper (syn->name [0]) && (*m)->begin_arguments () == (*m)->end_arguments ()) { // Static const methods are constants. // Methods without arguments which start with a capital letter are treated as constants // for backward compatibility - constants.push_back (RubyConstDescriptor ()); - constants.back ().klass = klass; - constants.back ().meth = *m; - constants.back ().name = (*m)->begin_synonyms ()->name; + m_constants.push_back (RubyConstDescriptor ()); + m_constants.back ().klass = klass; + m_constants.back ().meth = *m; + m_constants.back ().name = (*m)->begin_synonyms ()->name; } else if ((*m)->ret_type ().type () == gsi::T_object && (*m)->ret_type ().pass_obj () && syn->name == "new") { @@ -1640,96 +1700,150 @@ rba_init (RubyInterpreterPrivateData *d) // clean up the method table mt->finish (); - // Hint: we need to do static methods before the non-static ones because - // rb_define_module_function creates an private instance method. - // If we do the non-static methods afterwards we will make it a public once again. - // The order of the names will be "name(non-static), name(static), ..." because - // the static flag is the second member of the key (string, bool) pair. - for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) { + // NOTE: extensions can't carry methods - this is due to the method numbering scheme + // which can only handle direct base classes. So only constants are carried forward. + if (! is_extension) { - if (mt->is_static (mid)) { + // Hint: we need to do static methods before the non-static ones because + // rb_define_module_function creates an private instance method. + // If we do the non-static methods afterwards we will make it a public once again. + // The order of the names will be "name(non-static), name(static), ..." because + // the static flag is the second member of the key (string, bool) pair. + for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) { - tl_assert (mid < size_t (sizeof (method_adaptors) / sizeof (method_adaptors [0]))); + if (mt->is_static (mid)) { + + tl_assert (mid < size_t (sizeof (method_adaptors) / sizeof (method_adaptors [0]))); + + /* Note: Ruby does not support static protected functions, hence we have them (i.e. QThread::usleep). + * Do we silently create public ones from them: + if (mt->is_protected (mid)) { + tl::warn << "static '" << mt->name (mid) << "' method cannot be protected in class " << c->name (); + } + */ + + rb_define_module_function (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors[mid], -1); - /* Note: Ruby does not support static protected functions, hence we have them (i.e. QThread::usleep). - * Do we silently create public ones from them: - if (mt->is_protected (mid)) { - tl::warn << "static '" << mt->name (mid) << "' method cannot be protected in class " << c->name (); } - */ - rb_define_module_function (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors[mid], -1); + } + + for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) { + + if (mt->is_ctor (mid)) { + + tl_assert (mid < size_t (sizeof (method_adaptors_ctor) / sizeof (method_adaptors_ctor [0]))); + + if (! mt->is_protected (mid)) { + rb_define_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors_ctor[mid], -1); + } else { + // a protected constructor needs to be provided in both protected and non-protected mode + rb_define_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors_ctor[mid], -1); + rb_define_protected_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors_ctor[mid], -1); + } + + } else if (! mt->is_static (mid)) { + + tl_assert (mid < size_t (sizeof (method_adaptors) / sizeof (method_adaptors [0]))); + + if (! mt->is_protected (mid)) { + rb_define_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors[mid], -1); + } else { + rb_define_protected_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors[mid], -1); + } + + } + + if (mt->is_signal (mid)) { + + // We alias the signal name to an assignment, so the following can be done: + // x = object with signal "signal" + // x.signal = proc + // this will basically map to + // x.signal(proc) + // which will make proc the only receiver for the signal + rb_define_alias (klass, (mt->name (mid) + "=").c_str (), mt->name (mid).c_str ()); + + } + + if (mt->name (mid) == "to_s") { + #if HAVE_RUBY_VERSION_CODE>=20000 && defined(GSI_ALIAS_INSPECT) + // Ruby 2.x does no longer alias "inspect" to "to_s" automatically, so we have to do this: + rb_define_alias (klass, "inspect", "to_s"); + #endif + } else if (mt->name (mid) == "==") { + rb_define_alias (klass, "eql?", "=="); + } } } - for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) { + return klass; + } - if (mt->is_ctor (mid)) { + void make_constants () + { + for (auto c = m_constants.begin (); c != m_constants.end (); ++c) { - tl_assert (mid < size_t (sizeof (method_adaptors_ctor) / sizeof (method_adaptors_ctor [0]))); + try { - if (! mt->is_protected (mid)) { - rb_define_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors_ctor[mid], -1); - } else { - // a protected constructor needs to be provided in both protected and non-protected mode - rb_define_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors_ctor[mid], -1); - rb_define_protected_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors_ctor[mid], -1); - } + gsi::SerialArgs retlist (c->meth->retsize ()); + gsi::SerialArgs arglist (c->meth->argsize ()); + c->meth->call (0, arglist, retlist); + tl::Heap heap; + VALUE ret = pop_arg (c->meth->ret_type (), 0, retlist, heap); + rb_define_const (c->klass, c->name.c_str (), ret); - } else if (! mt->is_static (mid)) { - - tl_assert (mid < size_t (sizeof (method_adaptors) / sizeof (method_adaptors [0]))); - - if (! mt->is_protected (mid)) { - rb_define_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors[mid], -1); - } else { - rb_define_protected_method (klass, mt->name (mid).c_str (), (ruby_func) method_adaptors[mid], -1); - } - - } - - if (mt->is_signal (mid)) { - // We alias the signal name to an assignment, so the following can be done: - // x = object with signal "signal" - // x.signal = proc - // this will basically map to - // x.signal(proc) - // which will make proc the only receiver for the signal - rb_define_alias (klass, (mt->name (mid) + "=").c_str (), mt->name (mid).c_str ()); - } - - if (mt->name (mid) == "to_s") { -#if HAVE_RUBY_VERSION_CODE>=20000 && defined(GSI_ALIAS_INSPECT) - // Ruby 2.x does no longer alias "inspect" to "to_s" automatically, so we have to do this: - rb_define_alias (klass, "inspect", "to_s"); -#endif - } else if (mt->name (mid) == "==") { - rb_define_alias (klass, "eql?", "=="); + } catch (tl::Exception &ex) { + tl::warn << "Got exception '" << ex.msg () << "' while defining constant " << c->name; } } + } +private: + VALUE m_module; + std::vector m_constants; + std::map > m_extensions_for; + std::set m_extensions; + std::map > > m_links_for; +}; + +} + +static void +rba_init (RubyInterpreterPrivateData *d) +{ + VALUE module = rb_define_module ("RBA"); + + // initialize the locked object vault as a fast replacement for rb_gc_register_address/rb_gc_unregister_address. + rba::make_locked_object_vault (module); + + // save all constants for later (we cannot declare them while we are still producing classes + // because of the enum representative classes and enum constants are important) + std::vector constants; + + std::list sorted_classes = gsi::ClassBase::classes_in_definition_order (); + + RubyClassGenerator gen (module); + + // first pass: register the extensions + for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + if ((*c)->declaration () != *c) { + gen.register_extension (*c); + } + } + + // second pass: make the classes + for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + if ((*c)->declaration () == *c) { + gen.make_class (*c); + } } // now make the constants - for (std::vector ::const_iterator c = constants.begin (); c != constants.end (); ++c) { - - try { - - gsi::SerialArgs retlist (c->meth->retsize ()); - gsi::SerialArgs arglist (c->meth->argsize ()); - c->meth->call (0, arglist, retlist); - tl::Heap heap; - VALUE ret = pop_arg (c->meth->ret_type (), 0, retlist, heap); - rb_define_const (c->klass, c->name.c_str (), ret); - - } catch (tl::Exception &ex) { - tl::warn << "Got exception '" << ex.msg () << "' while defining constant " << c->name; - } - - } + gen.make_constants (); // define a signal representative class RBASignal SignalHandler::define_class (module, "RBASignal"); diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 1d86811b6..65344ce41 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -3012,6 +3012,23 @@ class Basic_TestClass < TestBase end + # Tests multi-base mixins (only constants and enums available) + def test_multiBaseMixins + + bb = RBA::BB::new # base classes B1,B2,B3 + bb.set1(17) # B1 + assert_equal(bb.get1, 17) # B1 + bb.set1(21) # B1 + + assert_equal(bb.get1, 21) # B1 + assert_equal(RBA::BB::C2, 17) # B2 + assert_equal(RBA::BB::C3, -1) # B3 + assert_equal(RBA::BB::E::E3B.to_i, 101) # B3 + assert_equal(bb.d3(RBA::BB::E::E3C, RBA::BB::E::E3A), -2) # BB with B3 enums + assert_equal(bb.d3(RBA::BB::E::E3A, RBA::BB::E::E3C), 2) # BB with B3 enums + + end + # Custom factory implemented in Ruby def test_80