From 9c0e94e63c37affe9b3573e54cab93d747137f96 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 7 Dec 2021 23:24:08 +0100 Subject: [PATCH 01/17] WIP: Allowing extension (mixin) classes Needed to represent QIODeviceBase as a second base class for QIOdevice, QFile etc. Additional base classes are supported but in a rather limited way. They only contribute constants like enums. No methods can be provided this way (a limitation of the method enumeration scheme which only supports one base classe) --- scripts/mkqtdecl6/mkqtdecl.conf | 7 + scripts/mkqtdecl_common/produce.rb | 92 +++-- scripts/mkqtdecl_common/reader_ext.rb | 42 ++- src/gsi/gsi/gsiClass.h | 2 +- src/gsi/gsi_test/gsiTest.cc | 27 ++ src/gsi/gsi_test/gsiTest.h | 31 ++ src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc | 5 + .../gsiDeclQXmlDefaultHandler.cc | 13 + .../qt6/QtGui/gsiDeclQOffscreenSurface.cc | 5 + .../qt6/QtGui/gsiDeclQPaintDeviceWindow.cc | 5 + src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc | 5 + src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc | 5 + .../qt6/QtWidgets/gsiDeclQAccessibleWidget.cc | 5 + .../qt6/QtWidgets/gsiDeclQGraphicsObject.cc | 6 + .../qt6/QtWidgets/gsiDeclQGraphicsWidget.cc | 5 + src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc | 5 + src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc | 5 + src/pya/pya/pyaModule.cc | 8 + src/rba/rba/rba.cc | 324 ++++++++++++------ testdata/ruby/basic_testcore.rb | 17 + 20 files changed, 471 insertions(+), 143 deletions(-) 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 From 1e795ebb6e1473610019a4714ce2bfef61586af1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 7 Dec 2021 23:43:40 +0100 Subject: [PATCH 02/17] Put in some blank lines for better readibility of generated Qt binding code --- scripts/mkqtdecl_common/produce.rb | 1 + src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc | 1 + src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc | 1 + src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc | 1 + src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc | 1 + src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc | 1 + src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc | 1 + src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc | 1 + src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc | 1 + src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc | 1 + src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc | 1 + src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc | 1 + 12 files changed, 12 insertions(+) diff --git a/scripts/mkqtdecl_common/produce.rb b/scripts/mkqtdecl_common/produce.rb index dfbc077f6..59fb97f6c 100755 --- a/scripts/mkqtdecl_common/produce.rb +++ b/scripts/mkqtdecl_common/produce.rb @@ -2492,6 +2492,7 @@ END # Produce the mixin base classes if ! mixin_base_classes.empty? + ofile.puts("") ofile.puts("// Additional base classes") ofile.puts("") mixin_base_classes.each do |bc| diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc b/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc index ebd882299..211b96080 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc @@ -846,6 +846,7 @@ qt_gsi::QtNativeClass decl_QIODevice (qtdecl_QObject (), "QtCore", "Q methods_QIODevice (), "@qt\n@brief Binding of QIODevice"); + // Additional base classes gsi::Class &qtdecl_QIODeviceBase (); diff --git a/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc b/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc index e87d211bd..021890ad0 100644 --- a/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc +++ b/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc @@ -776,6 +776,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc b/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc index 7817757a1..8a2743c79 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc @@ -313,6 +313,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc b/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc index 756ed7718..119ad5f89 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc @@ -297,6 +297,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc b/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc index 0129c0ab5..b8d88eef4 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc @@ -382,6 +382,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc b/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc index f654a43b0..ad25080ed 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc @@ -2206,6 +2206,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc index e37dc80c4..73987340e 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc @@ -450,6 +450,7 @@ gsi::Class decl_QAccessibleWidget (qtdecl_QAccessibleObject ( methods_QAccessibleWidget (), "@qt\n@brief Binding of QAccessibleWidget"); + // Additional base classes gsi::Class &qtdecl_QAccessibleActionInterface (); diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc index 87235928a..099cec461 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc @@ -243,6 +243,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc index fc008a543..2cbdee756 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc @@ -1374,6 +1374,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc index 59c675d3a..c8c777f38 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc @@ -1013,6 +1013,7 @@ 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 (); diff --git a/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc index dd739fa4b..506bac75d 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc @@ -4981,6 +4981,7 @@ 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 (); From 202ac55200520ff90f9f92315bb8a6d9919d654c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 8 Dec 2021 23:53:38 +0100 Subject: [PATCH 03/17] Preparations for multiple bases for Python objects --- src/pya/pya/pyaModule.cc | 315 ++++++++++++++++++++++++--------------- src/pya/pya/pyaModule.h | 47 +++++- 2 files changed, 242 insertions(+), 120 deletions(-) diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 1289e2a1c..3c09c805c 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -2308,6 +2308,12 @@ PythonModule::add_python_doc (const gsi::ClassBase & /*cls*/, const MethodTable } } +void +PythonModule::add_python_doc (const gsi::MethodBase *m, const std::string &doc) +{ + m_python_doc [m] += doc; +} + std::string PythonModule::python_doc (const gsi::MethodBase *method) { @@ -2354,90 +2360,81 @@ PythonModule::check (const char *mod_name) } } -void -PythonModule::make_classes (const char *mod_name) +namespace { - PyObject *module = mp_module.get (); - // Prepare an __all__ index for the module - - PythonRef all_list; - if (! PyObject_HasAttrString (module, "__all__")) { - all_list = PythonRef (PyList_New (0)); - PyObject_SetAttrString (module, "__all__", all_list.get ()); - } else { - all_list = PythonRef (PyObject_GetAttrString (module, "__all__")); +class PythonClassGenerator +{ +public: + PythonClassGenerator (PythonModule *module, PyObject *all_list) + : mp_module (module), m_all_list (all_list) + { + // .. nothing yet .. } - PyObject_SetAttrString (module, "__doc__", PythonRef (c2python (m_mod_description)).get ()); + // needs to be called before for each extension before the classes are made + void register_extension (const gsi::ClassBase *cls) + { + if (cls->name ().empty ()) { - // Build a class for descriptors for static attributes - PYAStaticAttributeDescriptorObject::make_class (module); + // got an extension + tl_assert (cls->parent ()); + m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); + m_extensions.insert (cls->declaration ()); - // Build a class for static/non-static dispatching descriptors - PYAAmbiguousMethodDispatcher::make_class (module); + } else { - // Build a class for iterators - PYAIteratorObject::make_class (module); + m_links_for [cls->parent ()->declaration ()].push_back (std::make_pair (cls->name (), cls->declaration ())); - // Build a class for signals - 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) { - // don't handle classes outside this module, but require them to be present - if (! PythonClassClientData::py_type (**c)) { - throw tl::Exception (tl::sprintf ("class %s.%s required from outside the module %s, but that module is not loaded", (*c)->module (), (*c)->name (), mod_name)); - } - continue; } + } - // 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) { - // ... @@@ 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 ()); - tl_assert (type != 0); - PythonRef attr ((PyObject *) type, false /*borrowed*/); - set_type_attr (parent_type, (*c)->name ().c_str (), attr); - continue; + PyTypeObject *make_class (const gsi::ClassBase *cls) + { + PyTypeObject *pt = PythonClassClientData::py_type (*cls); + if (pt != 0) { + return pt; } - // NOTE: we create the class as a heap object, since that way we can dynamically extend the objects - - // Create the actual class - - 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 ()); - tl_assert (pt != 0); - PyObject *base = (PyObject *) pt; - Py_INCREF (base); - PyTuple_SetItem (bases.get (), 0, base); - } else { - bases = PythonRef (PyTuple_New (0)); + + // mix-in unnamed extensions and get the base classes + + int n_bases = (cls->base () != 0 ? 1 : 0); + auto exts = m_extensions_for.find (cls); + if (exts != m_extensions_for.end ()) { +// @@@ n_bases += int (exts->second.size ()); } + bases = PythonRef (PyTuple_New (n_bases)); + + int ibase = 0; + if (cls->base () != 0) { + PyTypeObject *pt = make_class (cls->base ()); + PyObject *base = (PyObject *) pt; + Py_INCREF (base); + PyTuple_SetItem (bases.get (), ibase++, base); + } + +/*@@@ + if (exts != m_extensions_for.end ()) { + for (auto ie = exts->second.begin (); ie != exts->second.end (); ++ie) { + PyObject *base = (PyObject *) make_class (*ie); + Py_INCREF (base); + PyTuple_SetItem (bases.get (), ibase++, base); + } + } +@@@*/ + + // creates the type object + PythonRef dict (PyDict_New ()); - PyDict_SetItemString (dict.get (), "__module__", PythonRef (c2python (m_mod_name)).get ()); - PyDict_SetItemString (dict.get (), "__doc__", PythonRef (c2python ((*c)->doc ())).get ()); - PyDict_SetItemString (dict.get (), "__gsi_id__", PythonRef (c2python (m_classes.size () - 1)).get ()); + PyDict_SetItemString (dict.get (), "__module__", PythonRef (c2python (mp_module->mod_name ())).get ()); + PyDict_SetItemString (dict.get (), "__doc__", PythonRef (c2python (cls->doc ())).get ()); + PyDict_SetItemString (dict.get (), "__gsi_id__", PythonRef (c2python (mp_module->next_class_id ())).get ()); PythonRef args (PyTuple_New (3)); - PyTuple_SetItem (args.get (), 0, c2python ((*c)->name ())); + PyTuple_SetItem (args.get (), 0, c2python (cls->name ())); PyTuple_SetItem (args.get (), 1, bases.release ()); PyTuple_SetItem (args.get (), 2, dict.release ()); @@ -2455,30 +2452,41 @@ PythonModule::make_classes (const char *mod_name) type->tp_setattro = PyObject_GenericSetAttr; type->tp_getattro = PyObject_GenericGetAttr; - PythonClassClientData::initialize (**c, type); + PythonClassClientData::initialize (*cls, type); - tl_assert (cls_for_type (type) == *c); + mp_module->register_class (cls); + + tl_assert (mp_module->cls_for_type (type) == cls); // Add to the parent class as child class or add to module - if ((*c)->parent ()) { - tl_assert ((*c)->parent ()->declaration () != 0); - PyTypeObject *parent_type = PythonClassClientData::py_type (*(*c)->parent ()->declaration ()); + if (cls->parent ()) { + tl_assert (cls->parent ()->declaration () != 0); + PyTypeObject *parent_type = make_class (cls->parent ()->declaration ()); PythonRef attr ((PyObject *) type); - set_type_attr (parent_type, (*c)->name ().c_str (), attr); + set_type_attr (parent_type, cls->name ().c_str (), attr); } else { - PyList_Append (all_list.get (), PythonRef (c2python ((*c)->name ())).get ()); - PyModule_AddObject (module, (*c)->name ().c_str (), (PyObject *) type); + PyList_Append (m_all_list, PythonRef (c2python (cls->name ())).get ()); + PyModule_AddObject (mp_module->module (), cls->name ().c_str (), (PyObject *) type); } - // ... @@@ add the named extensions as attributes (see above) ... + // 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) { + PyTypeObject *linked_type = make_class (il->second); + PythonRef attr ((PyObject *) linked_type, false /*borrowed*/); + set_type_attr (type, il->first.c_str (), attr); + } + } // Build the attributes now ... - MethodTable *mt = MethodTable::method_table_by_class (*c); + MethodTable *mt = MethodTable::method_table_by_class (cls); // signals are translated into the setters and getters - for (gsi::ClassBase::method_iterator m = (*c)->begin_methods (); m != (*c)->end_methods (); ++m) { + for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) { if ((*m)->is_signal ()) { for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { mt->add_getter (syn->name, *m); @@ -2488,7 +2496,7 @@ PythonModule::make_classes (const char *mod_name) } // first add getters and setters - for (gsi::ClassBase::method_iterator m = (*c)->begin_methods (); m != (*c)->end_methods (); ++m) { + for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) { if (! (*m)->is_callback ()) { for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { if (syn->is_getter) { @@ -2501,7 +2509,7 @@ PythonModule::make_classes (const char *mod_name) } // then add normal methods - on name clash with properties make them a getter - for (gsi::ClassBase::method_iterator m = (*c)->begin_methods (); m != (*c)->end_methods (); ++m) { + for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) { if (! (*m)->is_callback () && ! (*m)->is_signal ()) { for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { if (! syn->is_getter && ! syn->is_setter) { @@ -2536,10 +2544,10 @@ PythonModule::make_classes (const char *mod_name) const std::string &name = mt->property_name (mid); // look for the real getter and setter, also look in the base classes - const gsi::ClassBase *cls = *c; - while ((cls = cls->base ()) != 0 && (begin_setters == end_setters || begin_getters == end_getters)) { + const gsi::ClassBase *icls = cls; + while ((icls = icls->base ()) != 0 && (begin_setters == end_setters || begin_getters == end_getters)) { - const MethodTable *mt_base = MethodTable::method_table_by_class (cls); + const MethodTable *mt_base = MethodTable::method_table_by_class (icls); tl_assert (mt_base); std::pair t = mt_base->find_property (is_static, name); if (t.first) { @@ -2566,7 +2574,7 @@ PythonModule::make_classes (const char *mod_name) doc += "\n\n"; } doc += (*m)->doc (); - m_python_doc [*m] += tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter.\n\n")), name); + mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter.\n\n")), name)); } for (MethodTableEntry::method_iterator m = begin_setters; m != end_setters; ++m) { @@ -2574,7 +2582,7 @@ PythonModule::make_classes (const char *mod_name) doc += "\n\n"; } doc += (*m)->doc (); - m_python_doc [*m] += tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter.\n\n")), name); + mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter.\n\n")), name)); } PythonRef attr; @@ -2582,18 +2590,18 @@ PythonModule::make_classes (const char *mod_name) if (! is_static) { // non-static attribute getters/setters - PyGetSetDef *getset = make_getset_def (); - getset->name = make_string (name); + PyGetSetDef *getset = mp_module->make_getset_def (); + getset->name = mp_module->make_string (name); getset->get = begin_getters != end_getters ? &property_getter_func : NULL; getset->set = begin_setters != end_setters ? &property_setter_func : NULL; - getset->doc = make_string (doc); + getset->doc = mp_module->make_string (doc); getset->closure = make_closure (getter_mid, setter_mid); attr = PythonRef (PyDescr_NewGetSet (type, getset)); } else { - PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (make_string (name)); + PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name)); desc->type = type; desc->getter = begin_getters != end_getters ? property_getter_adaptors[getter_mid] : NULL; @@ -2628,10 +2636,10 @@ PythonModule::make_classes (const char *mod_name) // drop non-standard names if (tl::verbosity () >= 20) { - tl::warn << tl::to_string (tr ("Class ")) << (*c)->name () << ": " << tl::to_string (tr ("no Python mapping for method ")) << mt->name (mid); + tl::warn << tl::to_string (tr ("Class ")) << cls->name () << ": " << tl::to_string (tr ("no Python mapping for method ")) << mt->name (mid); } - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is not available for Python"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is not available for Python"))); } else { @@ -2658,7 +2666,7 @@ PythonModule::make_classes (const char *mod_name) // drop non-standard names if (tl::verbosity () >= 20) { - tl::warn << tl::to_string (tr ("Class ")) << (*c)->name () << ": " << tl::to_string (tr ("no Python mapping for method (reserved word) ")) << name; + tl::warn << tl::to_string (tr ("Class ")) << cls->name () << ": " << tl::to_string (tr ("no Python mapping for method (reserved word) ")) << name; } name += "_"; @@ -2666,7 +2674,7 @@ PythonModule::make_classes (const char *mod_name) } if (name != raw_name) { - add_python_doc (**c, mt, int (mid), tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), name)); + mp_module->add_python_doc (*cls, mt, int (mid), tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), name)); } // create documentation @@ -2695,41 +2703,41 @@ PythonModule::make_classes (const char *mod_name) bool alias_inspect = false; #endif if (alias_inspect) { - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'"))); alt_names.push_back ("__repr__"); } else { - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)'"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)'"))); } } else if (name == "hash" && m_first->compatible_with_num_args (0)) { // The hash method is also routed via the tp_hash implementation alt_names.push_back ("__hash__"); - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is also available as 'hash(object)'"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'hash(object)'"))); } else if (name == "inspect" && m_first->compatible_with_num_args (0)) { // The str method is also routed via the tp_str implementation - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is also available as 'repr(object)'"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'repr(object)'"))); alt_names.push_back ("__repr__"); } else if (name == "size" && m_first->compatible_with_num_args (0)) { // The size method is also routed via the sequence methods protocol if there // is a [] function - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is also available as 'len(object)'"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'len(object)'"))); alt_names.push_back ("__len__"); } else if (name == "each" && m_first->compatible_with_num_args (0) && m_first->ret_type ().is_iter ()) { // each makes the object iterable - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method enables iteration of the object"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method enables iteration of the object"))); alt_names.push_back ("__iter__"); } else if (name == "__mul__") { // Adding right multiplication // Rationale: if pyaObj * x works, so should x * pyaObj - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is also available as '__mul__'"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as '__mul__'"))); alt_names.push_back ("__rmul__"); } @@ -2737,10 +2745,10 @@ PythonModule::make_classes (const char *mod_name) // needs registration under an alternative name to enable special protocols - PyMethodDef *method = make_method_def (); - method->ml_name = make_string (*an); + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = mp_module->make_string (*an); method->ml_meth = (PyCFunction) method_adaptors[mid]; - method->ml_doc = make_string (doc); + method->ml_doc = mp_module->make_string (doc); method->ml_flags = METH_VARARGS; PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); @@ -2748,10 +2756,10 @@ PythonModule::make_classes (const char *mod_name) } - PyMethodDef *method = make_method_def (); - method->ml_name = make_string (name); + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = mp_module->make_string (name); method->ml_meth = (PyCFunction) method_adaptors[mid]; - method->ml_doc = make_string (doc); + method->ml_doc = mp_module->make_string (doc); method->ml_flags = METH_VARARGS; PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); @@ -2762,7 +2770,7 @@ PythonModule::make_classes (const char *mod_name) if ((mt->end (mid) - mt->begin (mid)) == 1 && m_first->begin_arguments () == m_first->end_arguments ()) { // static methods without arguments which start with a capital letter are treated as constants - PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (make_string (name)); + PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name)); desc->type = type; desc->getter = method_adaptors[mid]; @@ -2770,8 +2778,8 @@ PythonModule::make_classes (const char *mod_name) set_type_attr (type, name, attr); } else if (tl::verbosity () >= 20) { - tl::warn << "Upper case method name encountered which cannot be used as a Python constant (more than one overload or at least one argument): " << (*c)->name () << "." << name; - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This attribute is not available for Python"))); + tl::warn << "Upper case method name encountered which cannot be used as a Python constant (more than one overload or at least one argument): " << cls->name () << "." << name; + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This attribute is not available for Python"))); } } else { @@ -2779,12 +2787,12 @@ PythonModule::make_classes (const char *mod_name) if (m_first->ret_type ().type () == gsi::T_object && m_first->ret_type ().pass_obj () && name == "new") { // The constructor is also routed via the pya_object_init implementation - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is the default initializer of the object"))); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is the default initializer of the object"))); - PyMethodDef *method = make_method_def (); + PyMethodDef *method = mp_module->make_method_def (); method->ml_name = "__init__"; method->ml_meth = (PyCFunction) method_init_adaptors[mid]; - method->ml_doc = make_string (doc); + method->ml_doc = mp_module->make_string (doc); method->ml_flags = METH_VARARGS; PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); @@ -2792,10 +2800,10 @@ PythonModule::make_classes (const char *mod_name) } - PyMethodDef *method = make_method_def (); - method->ml_name = make_string (name); + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = mp_module->make_string (name); method->ml_meth = (PyCFunction) method_adaptors[mid]; - method->ml_doc = make_string (doc); + method->ml_doc = mp_module->make_string (doc); method->ml_flags = METH_VARARGS | METH_CLASS; PythonRef attr = PythonRef (PyDescr_NewClassMethod (type, method)); @@ -2831,7 +2839,7 @@ PythonModule::make_classes (const char *mod_name) if (! has_ne) { // Add a definition for "__ne__" - PyMethodDef *method = make_method_def (); + PyMethodDef *method = mp_module->make_method_def (); method->ml_name = "__ne__"; method->ml_meth = &object_default_ne_impl; method->ml_flags = METH_VARARGS; @@ -2844,7 +2852,7 @@ PythonModule::make_classes (const char *mod_name) if (has_lt && ! has_le) { // Add a definition for "__le__" - PyMethodDef *method = make_method_def (); + PyMethodDef *method = mp_module->make_method_def (); method->ml_name = "__le__"; method->ml_meth = &object_default_le_impl; method->ml_flags = METH_VARARGS; @@ -2857,7 +2865,7 @@ PythonModule::make_classes (const char *mod_name) if (has_lt && ! has_gt) { // Add a definition for "__gt__" - PyMethodDef *method = make_method_def (); + PyMethodDef *method = mp_module->make_method_def (); method->ml_name = "__gt__"; method->ml_meth = &object_default_gt_impl; method->ml_flags = METH_VARARGS; @@ -2870,7 +2878,7 @@ PythonModule::make_classes (const char *mod_name) if (has_lt && ! has_ge) { // Add a definition for "__ge__" - PyMethodDef *method = make_method_def (); + PyMethodDef *method = mp_module->make_method_def (); method->ml_name = "__ge__"; method->ml_meth = &object_default_ge_impl; method->ml_flags = METH_VARARGS; @@ -2895,7 +2903,7 @@ PythonModule::make_classes (const char *mod_name) Py_XDECREF (attr_class); PyErr_Clear (); - tl::warn << "Unable to install a static/non-static disambiguator for " << *a << " in class " << (*c)->name (); + tl::warn << "Unable to install a static/non-static disambiguator for " << *a << " in class " << cls->name (); } else { @@ -2910,6 +2918,75 @@ PythonModule::make_classes (const char *mod_name) mt->finish (); + return type; + } + +private: + PythonModule *mp_module; + PyObject *m_all_list; + std::map > m_extensions_for; + std::set m_extensions; + std::map > > m_links_for; +}; + +} + +void +PythonModule::make_classes (const char *mod_name) +{ + PyObject *module = mp_module.get (); + + // Prepare an __all__ index for the module + + PythonRef all_list; + if (! PyObject_HasAttrString (module, "__all__")) { + all_list = PythonRef (PyList_New (0)); + PyObject_SetAttrString (module, "__all__", all_list.get ()); + } else { + all_list = PythonRef (PyObject_GetAttrString (module, "__all__")); + } + + PyObject_SetAttrString (module, "__doc__", PythonRef (c2python (m_mod_description)).get ()); + + // Build a class for descriptors for static attributes + PYAStaticAttributeDescriptorObject::make_class (module); + + // Build a class for static/non-static dispatching descriptors + PYAAmbiguousMethodDispatcher::make_class (module); + + // Build a class for iterators + PYAIteratorObject::make_class (module); + + // Build a class for signals + PYASignal::make_class (module); + + std::list sorted_classes = gsi::ClassBase::classes_in_definition_order (mod_name); + + PythonClassGenerator gen (this, all_list.get ()); + + if (mod_name) { + for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + if ((*c)->module () != mod_name) { + // don't handle classes outside this module, but require them to be present + if (! PythonClassClientData::py_type (**c)) { + throw tl::Exception (tl::sprintf ("class %s.%s required from outside the module %s, but that module is not loaded", (*c)->module (), (*c)->name (), mod_name)); + } + } + } + } + + // first pass: register the extensions + for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + if ((*c)->declaration () != *c && (! mod_name || (*c)->module () == mod_name)) { + gen.register_extension (*c); + } + } + + // second pass: make the classes + for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + if ((*c)->declaration () == *c && (! mod_name || (*c)->module () == mod_name)) { + gen.make_class (*c); + } } } diff --git a/src/pya/pya/pyaModule.h b/src/pya/pya/pyaModule.h index beb510926..1656a11e4 100644 --- a/src/pya/pya/pyaModule.h +++ b/src/pya/pya/pyaModule.h @@ -108,11 +108,56 @@ public: */ PyObject *take_module (); -private: + /** + * @brief Gets the module name + */ + const std::string &mod_name () const + { + return m_mod_name; + } + + /** + * @brief Adds the given documentation for the given class and method + */ void add_python_doc (const gsi::ClassBase &cls, const MethodTable *mt, int mid, const std::string &doc); + + /** + * @brief Adds the given documentation for the given class + */ + void add_python_doc (const gsi::MethodBase *m, const std::string &doc); + + /** + * @brief Creates a method definition + */ PyMethodDef *make_method_def (); + + /** + * @brief Creates a property getter/setter pair + */ PyGetSetDef *make_getset_def (); + + /** + * @brief Creates a string with backup storage + */ char *make_string (const std::string &s); + + /** + * @brief Gets the next available class ID + */ + size_t next_class_id () const + { + return m_classes.size (); + } + + /** + * @brief Register the class + */ + void register_class (const gsi::ClassBase *cls) + { + m_classes.push_back (cls); + } + +private: static void check (const char *mod_name); std::list m_string_heap; From f15db66fc44336eb3478c9faecb4a3e3bea499f5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 10 Dec 2021 00:53:51 +0100 Subject: [PATCH 04/17] Providing a different implementation of the multi-base mixin concept for Python to support multiple bases in Qt binding (e.g. QIODeviceBase) --- src/pya/pya/pyaModule.cc | 632 +++++++++++++++++++-------------------- src/pya/pya/pyaModule.h | 2 - src/pya/pya/pyaObject.cc | 12 + src/pya/pya/pyaObject.h | 16 +- testdata/python/basic.py | 17 +- 5 files changed, 347 insertions(+), 332 deletions(-) diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 3c09c805c..6aed6692c 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -116,6 +116,44 @@ public: MethodTable (const gsi::ClassBase *cls_decl) : m_method_offset (0), m_property_offset (0), mp_cls_decl (cls_decl) { + // signals are translated into the setters and getters + for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) { + if ((*m)->is_signal ()) { + for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { + add_getter (syn->name, *m); + add_setter (syn->name, *m); + } + } + } + + // first add getters and setters + for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) { + if (! (*m)->is_callback ()) { + for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { + if (syn->is_getter) { + add_getter (syn->name, *m); + } else if (syn->is_setter) { + add_setter (syn->name, *m); + } + } + } + } + + // then add normal methods - on name clash with properties make them a getter + for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) { + if (! (*m)->is_callback () && ! (*m)->is_signal ()) { + for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { + if (! syn->is_getter && ! syn->is_setter) { + if ((*m)->end_arguments () - (*m)->begin_arguments () == 0 && find_property ((*m)->is_static (), syn->name).first) { + add_getter (syn->name, *m); + } else { + add_method (syn->name, *m); + } + } + } + } + } + if (cls_decl->base ()) { const MethodTable *base_mt = method_table_by_class (cls_decl->base ()); tl_assert (base_mt); @@ -369,24 +407,34 @@ private: struct PythonClassClientData : public gsi::PerClassClientSpecificData { - PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type) - : py_type_object (_py_type), method_table (_cls) + PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type, PyTypeObject *_py_type_static) + : py_type_object (_py_type), py_type_object_static (_py_type_static), method_table (_cls) { // .. nothing yet .. } PyTypeObject *py_type_object; + PyTypeObject *py_type_object_static; MethodTable method_table; - static PyTypeObject *py_type (const gsi::ClassBase &cls_decl) + static PyTypeObject *py_type (const gsi::ClassBase &cls_decl, bool as_static) { PythonClassClientData *cd = dynamic_cast(cls_decl.data (gsi::ClientIndex::Python)); - return cd ? cd->py_type_object : 0; + return cd ? (as_static ? cd->py_type_object_static : cd->py_type_object) : 0; } - static void initialize (const gsi::ClassBase &cls_decl, PyTypeObject *py_type) + static void initialize (const gsi::ClassBase &cls_decl, PyTypeObject *py_type, bool as_static) { - cls_decl.set_data (gsi::ClientIndex::Python, new PythonClassClientData (&cls_decl, py_type)); + PythonClassClientData *cd = dynamic_cast(cls_decl.data (gsi::ClientIndex::Python)); + if (cd) { + if (as_static) { + cd->py_type_object_static = py_type; + } else { + cd->py_type_object = py_type; + } + } else { + cls_decl.set_data (gsi::ClientIndex::Python, new PythonClassClientData (&cls_decl, as_static ? NULL : py_type, as_static ? py_type : NULL)); + } } }; @@ -2224,7 +2272,6 @@ PythonModule::init (const char *mod_name, const char *description) // do some checks before we create the module tl_assert (mod_name != 0); tl_assert (mp_module.get () == 0); - check (mod_name); m_mod_name = pymod_name + "." + mod_name; m_mod_description = description; @@ -2269,7 +2316,6 @@ PythonModule::init (const char *mod_name, PyObject *module) { // do some checks before we create the module tl_assert (mp_module.get () == 0); - check (mod_name); m_mod_name = mod_name; mp_module = PythonRef (module); @@ -2325,41 +2371,6 @@ PythonModule::python_doc (const gsi::MethodBase *method) } } -void -PythonModule::check (const char *mod_name) -{ - if (! mod_name) { - return; - } - - // Check whether the new classes are self-contained within this module - for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) { - - if (c->module () != mod_name) { - // don't handle classes outside this module - continue; - } - - if (PythonClassClientData::py_type (*c)) { - // don't handle classes twice - continue; - } - - // All child classes must originate from this module or be known already - for (tl::weak_collection::const_iterator cc = c->begin_child_classes (); cc != c->end_child_classes (); ++cc) { - if (! PythonClassClientData::py_type (*cc->declaration ()) && cc->module () != mod_name) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Class %s from module %s depends on %s.%s (try 'import %s' before 'import %s')")), c->name (), mod_name, cc->module (), cc->name (), pymod_name + "." + cc->module (), pymod_name + "." + mod_name)); - } - } - - // Same for base class - if (c->base () && ! PythonClassClientData::py_type (*c->base ()) && c->base ()->module () != mod_name) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Class %s from module %s depends on %s.%s (try 'import %s' before 'import %s')")), c->name (), mod_name, c->base ()->module (), c->base ()->name (), pymod_name + "." + c->base ()->module (), pymod_name + "." + mod_name)); - } - - } -} - namespace { @@ -2380,7 +2391,6 @@ public: // got an extension tl_assert (cls->parent ()); m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); - m_extensions.insert (cls->declaration ()); } else { @@ -2389,9 +2399,15 @@ public: } } - PyTypeObject *make_class (const gsi::ClassBase *cls) + PyTypeObject *make_class (const gsi::ClassBase *cls, bool as_static) { - PyTypeObject *pt = PythonClassClientData::py_type (*cls); + // NOTE: with as_static = true, this method produces a mixin. This is a class entirely consisting + // of static constants and child classes only. It can be mixed into an existing class for emulation + // additional base classes. + // Everything else, like properties and methods will not work as the method enumeration scheme is + // not capable of handling more than a single base class. + + PyTypeObject *pt = PythonClassClientData::py_type (*cls, as_static); if (pt != 0) { return pt; } @@ -2403,28 +2419,26 @@ public: int n_bases = (cls->base () != 0 ? 1 : 0); auto exts = m_extensions_for.find (cls); if (exts != m_extensions_for.end ()) { -// @@@ n_bases += int (exts->second.size ()); + n_bases += int (exts->second.size ()); } bases = PythonRef (PyTuple_New (n_bases)); int ibase = 0; if (cls->base () != 0) { - PyTypeObject *pt = make_class (cls->base ()); + PyTypeObject *pt = make_class (cls->base (), as_static); PyObject *base = (PyObject *) pt; Py_INCREF (base); PyTuple_SetItem (bases.get (), ibase++, base); } -/*@@@ if (exts != m_extensions_for.end ()) { for (auto ie = exts->second.begin (); ie != exts->second.end (); ++ie) { - PyObject *base = (PyObject *) make_class (*ie); + PyObject *base = (PyObject *) make_class (*ie, true); Py_INCREF (base); PyTuple_SetItem (bases.get (), ibase++, base); } } -@@@*/ // creates the type object @@ -2434,7 +2448,11 @@ public: PyDict_SetItemString (dict.get (), "__gsi_id__", PythonRef (c2python (mp_module->next_class_id ())).get ()); PythonRef args (PyTuple_New (3)); - PyTuple_SetItem (args.get (), 0, c2python (cls->name ())); + if (! as_static) { + PyTuple_SetItem (args.get (), 0, c2python (cls->name ())); + } else { + PyTuple_SetItem (args.get (), 0, c2python (cls->name () + "_Mixin")); + } PyTuple_SetItem (args.get (), 1, bases.release ()); PyTuple_SetItem (args.get (), 2, dict.release ()); @@ -2445,37 +2463,42 @@ public: } // Customize - type->tp_basicsize += sizeof (PYAObjectBase); - type->tp_init = &pya_object_init; - type->tp_new = &pya_object_new; - type->tp_dealloc = (destructor) &pya_object_deallocate; - type->tp_setattro = PyObject_GenericSetAttr; - type->tp_getattro = PyObject_GenericGetAttr; + if (! as_static) { + type->tp_basicsize += sizeof (PYAObjectBase); + type->tp_init = &pya_object_init; + type->tp_new = &pya_object_new; + type->tp_dealloc = (destructor) &pya_object_deallocate; + type->tp_setattro = PyObject_GenericSetAttr; + type->tp_getattro = PyObject_GenericGetAttr; + } - PythonClassClientData::initialize (*cls, type); + PythonClassClientData::initialize (*cls, type, as_static); mp_module->register_class (cls); tl_assert (mp_module->cls_for_type (type) == cls); - // Add to the parent class as child class or add to module + // add to the parent class as child class or add to module - if (cls->parent ()) { - tl_assert (cls->parent ()->declaration () != 0); - PyTypeObject *parent_type = make_class (cls->parent ()->declaration ()); - PythonRef attr ((PyObject *) type); - set_type_attr (parent_type, cls->name ().c_str (), attr); - } else { + if (! cls->parent ()) { PyList_Append (m_all_list, PythonRef (c2python (cls->name ())).get ()); PyModule_AddObject (mp_module->module (), cls->name ().c_str (), (PyObject *) type); } + // produce the child classes + + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + PyTypeObject *child_class = make_class (cc.operator-> (), as_static); + PythonRef attr ((PyObject *) child_class, false /*borrowed*/); + set_type_attr (type, cc->name ().c_str (), attr); + } + // 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) { - PyTypeObject *linked_type = make_class (il->second); + PyTypeObject *linked_type = make_class (il->second, false); PythonRef attr ((PyObject *) linked_type, false /*borrowed*/); set_type_attr (type, il->first.c_str (), attr); } @@ -2485,133 +2508,99 @@ public: MethodTable *mt = MethodTable::method_table_by_class (cls); - // signals are translated into the setters and getters - for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) { - if ((*m)->is_signal ()) { - for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { - mt->add_getter (syn->name, *m); - mt->add_setter (syn->name, *m); - } - } - } - - // first add getters and setters - for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) { - if (! (*m)->is_callback ()) { - for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { - if (syn->is_getter) { - mt->add_getter (syn->name, *m); - } else if (syn->is_setter) { - mt->add_setter (syn->name, *m); - } - } - } - } - - // then add normal methods - on name clash with properties make them a getter - for (gsi::ClassBase::method_iterator m = cls->begin_methods (); m != cls->end_methods (); ++m) { - if (! (*m)->is_callback () && ! (*m)->is_signal ()) { - for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { - if (! syn->is_getter && ! syn->is_setter) { - if ((*m)->end_arguments () - (*m)->begin_arguments () == 0 && mt->find_property ((*m)->is_static (), syn->name).first) { - mt->add_getter (syn->name, *m); - } else { - mt->add_method (syn->name, *m); - } - } - } - } - } - // produce the properties - for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++mid) { + if (! as_static) { - MethodTableEntry::method_iterator begin_setters = mt->begin_setters (mid); - MethodTableEntry::method_iterator end_setters = mt->end_setters (mid); - MethodTableEntry::method_iterator begin_getters = mt->begin_getters (mid); - MethodTableEntry::method_iterator end_getters = mt->end_getters (mid); - int setter_mid = begin_setters != end_setters ? int (mid) : -1; - int getter_mid = begin_getters != end_getters ? int (mid) : -1; + for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++mid) { - bool is_static = false; - if (begin_setters != end_setters) { - is_static = (*begin_setters)->is_static (); - } else if (begin_getters != end_getters) { - is_static = (*begin_getters)->is_static (); - } + MethodTableEntry::method_iterator begin_setters = mt->begin_setters (mid); + MethodTableEntry::method_iterator end_setters = mt->end_setters (mid); + MethodTableEntry::method_iterator begin_getters = mt->begin_getters (mid); + MethodTableEntry::method_iterator end_getters = mt->end_getters (mid); + int setter_mid = begin_setters != end_setters ? int (mid) : -1; + int getter_mid = begin_getters != end_getters ? int (mid) : -1; - const std::string &name = mt->property_name (mid); + bool is_static = false; + if (begin_setters != end_setters) { + is_static = (*begin_setters)->is_static (); + } else if (begin_getters != end_getters) { + is_static = (*begin_getters)->is_static (); + } - // look for the real getter and setter, also look in the base classes - const gsi::ClassBase *icls = cls; - while ((icls = icls->base ()) != 0 && (begin_setters == end_setters || begin_getters == end_getters)) { + const std::string &name = mt->property_name (mid); - const MethodTable *mt_base = MethodTable::method_table_by_class (icls); - tl_assert (mt_base); - std::pair t = mt_base->find_property (is_static, name); - if (t.first) { - if (begin_setters == end_setters && mt_base->begin_setters (t.second) != mt_base->end_setters (t.second)) { - setter_mid = int (t.second); - begin_setters = mt_base->begin_setters (t.second); - end_setters = mt_base->end_setters (t.second); + // look for the real getter and setter, also look in the base classes + const gsi::ClassBase *icls = cls; + while ((icls = icls->base ()) != 0 && (begin_setters == end_setters || begin_getters == end_getters)) { + + const MethodTable *mt_base = MethodTable::method_table_by_class (icls); + tl_assert (mt_base); + std::pair t = mt_base->find_property (is_static, name); + if (t.first) { + if (begin_setters == end_setters && mt_base->begin_setters (t.second) != mt_base->end_setters (t.second)) { + setter_mid = int (t.second); + begin_setters = mt_base->begin_setters (t.second); + end_setters = mt_base->end_setters (t.second); + } + if (begin_getters == end_getters && mt_base->begin_getters (t.second) != mt_base->end_getters (t.second)) { + getter_mid = int (t.second); + begin_getters = mt_base->begin_getters (t.second); + end_getters = mt_base->end_getters (t.second); + } } - if (begin_getters == end_getters && mt_base->begin_getters (t.second) != mt_base->end_getters (t.second)) { - getter_mid = int (t.second); - begin_getters = mt_base->begin_getters (t.second); - end_getters = mt_base->end_getters (t.second); + + } + + std::string doc; + + // add getter and setter documentation, create specific Python documentation + + for (MethodTableEntry::method_iterator m = begin_getters; m != end_getters; ++m) { + if (! doc.empty ()) { + doc += "\n\n"; } + doc += (*m)->doc (); + mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter.\n\n")), name)); } - } - - std::string doc; - - // add getter and setter documentation, create specific Python documentation - - for (MethodTableEntry::method_iterator m = begin_getters; m != end_getters; ++m) { - if (! doc.empty ()) { - doc += "\n\n"; + for (MethodTableEntry::method_iterator m = begin_setters; m != end_setters; ++m) { + if (! doc.empty ()) { + doc += "\n\n"; + } + doc += (*m)->doc (); + mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter.\n\n")), name)); } - doc += (*m)->doc (); - mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter.\n\n")), name)); - } - for (MethodTableEntry::method_iterator m = begin_setters; m != end_setters; ++m) { - if (! doc.empty ()) { - doc += "\n\n"; + PythonRef attr; + + if (! is_static) { + + // non-static attribute getters/setters + PyGetSetDef *getset = mp_module->make_getset_def (); + getset->name = mp_module->make_string (name); + getset->get = begin_getters != end_getters ? &property_getter_func : NULL; + getset->set = begin_setters != end_setters ? &property_setter_func : NULL; + getset->doc = mp_module->make_string (doc); + getset->closure = make_closure (getter_mid, setter_mid); + + attr = PythonRef (PyDescr_NewGetSet (type, getset)); + + } else { + + PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name)); + + desc->type = type; + desc->getter = begin_getters != end_getters ? property_getter_adaptors[getter_mid] : NULL; + desc->setter = begin_setters != end_setters ? property_setter_adaptors[setter_mid] : NULL; + attr = PythonRef (desc); + } - doc += (*m)->doc (); - mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter.\n\n")), name)); - } - PythonRef attr; - - if (! is_static) { - - // non-static attribute getters/setters - PyGetSetDef *getset = mp_module->make_getset_def (); - getset->name = mp_module->make_string (name); - getset->get = begin_getters != end_getters ? &property_getter_func : NULL; - getset->set = begin_setters != end_setters ? &property_setter_func : NULL; - getset->doc = mp_module->make_string (doc); - getset->closure = make_closure (getter_mid, setter_mid); - - attr = PythonRef (PyDescr_NewGetSet (type, getset)); - - } else { - - PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name)); - - desc->type = type; - desc->getter = begin_getters != end_getters ? property_getter_adaptors[getter_mid] : NULL; - desc->setter = begin_setters != end_setters ? property_setter_adaptors[setter_mid] : NULL; - attr = PythonRef (desc); + set_type_attr (type, name, attr); } - set_type_attr (type, name, attr); - } // collect the names which have been disambiguated static/non-static wise @@ -2691,80 +2680,84 @@ public: tl_assert (mid < sizeof (method_adaptors) / sizeof (method_adaptors[0])); if (! mt->is_static (mid)) { - std::vector alt_names; + if (! as_static) { - if (name == "to_s" && m_first->compatible_with_num_args (0)) { + std::vector alt_names; - // The str method is also routed via the tp_str implementation - alt_names.push_back ("__str__"); + if (name == "to_s" && m_first->compatible_with_num_args (0)) { + + // The str method is also routed via the tp_str implementation + alt_names.push_back ("__str__"); #if GSI_ALIAS_INSPECT - bool alias_inspect = ! has_inspect; + bool alias_inspect = ! has_inspect; #else - bool alias_inspect = false; + bool alias_inspect = false; #endif - if (alias_inspect) { - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'"))); + if (alias_inspect) { + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'"))); + alt_names.push_back ("__repr__"); + } else { + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)'"))); + } + + } else if (name == "hash" && m_first->compatible_with_num_args (0)) { + + // The hash method is also routed via the tp_hash implementation + alt_names.push_back ("__hash__"); + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'hash(object)'"))); + + } else if (name == "inspect" && m_first->compatible_with_num_args (0)) { + + // The str method is also routed via the tp_str implementation + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'repr(object)'"))); alt_names.push_back ("__repr__"); - } else { - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)'"))); + + } else if (name == "size" && m_first->compatible_with_num_args (0)) { + + // The size method is also routed via the sequence methods protocol if there + // is a [] function + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'len(object)'"))); + alt_names.push_back ("__len__"); + + } else if (name == "each" && m_first->compatible_with_num_args (0) && m_first->ret_type ().is_iter ()) { + + // each makes the object iterable + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method enables iteration of the object"))); + alt_names.push_back ("__iter__"); + + } else if (name == "__mul__") { + // Adding right multiplication + // Rationale: if pyaObj * x works, so should x * pyaObj + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as '__mul__'"))); + alt_names.push_back ("__rmul__"); } - } else if (name == "hash" && m_first->compatible_with_num_args (0)) { + for (std::vector ::const_iterator an = alt_names.begin (); an != alt_names.end (); ++an) { - // The hash method is also routed via the tp_hash implementation - alt_names.push_back ("__hash__"); - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'hash(object)'"))); + // needs registration under an alternative name to enable special protocols - } else if (name == "inspect" && m_first->compatible_with_num_args (0)) { + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = mp_module->make_string (*an); + method->ml_meth = (PyCFunction) method_adaptors[mid]; + method->ml_doc = mp_module->make_string (doc); + method->ml_flags = METH_VARARGS; - // The str method is also routed via the tp_str implementation - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'repr(object)'"))); - alt_names.push_back ("__repr__"); + PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); + set_type_attr (type, *an, attr); - } else if (name == "size" && m_first->compatible_with_num_args (0)) { - - // The size method is also routed via the sequence methods protocol if there - // is a [] function - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as 'len(object)'"))); - alt_names.push_back ("__len__"); - - } else if (name == "each" && m_first->compatible_with_num_args (0) && m_first->ret_type ().is_iter ()) { - - // each makes the object iterable - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method enables iteration of the object"))); - alt_names.push_back ("__iter__"); - - } else if (name == "__mul__") { - // Adding right multiplication - // Rationale: if pyaObj * x works, so should x * pyaObj - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is also available as '__mul__'"))); - alt_names.push_back ("__rmul__"); - } - - for (std::vector ::const_iterator an = alt_names.begin (); an != alt_names.end (); ++an) { - - // needs registration under an alternative name to enable special protocols + } PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = mp_module->make_string (*an); + method->ml_name = mp_module->make_string (name); method->ml_meth = (PyCFunction) method_adaptors[mid]; method->ml_doc = mp_module->make_string (doc); method->ml_flags = METH_VARARGS; PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, *an, attr); + set_type_attr (type, name, attr); } - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = mp_module->make_string (name); - method->ml_meth = (PyCFunction) method_adaptors[mid]; - method->ml_doc = mp_module->make_string (doc); - method->ml_flags = METH_VARARGS; - - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, name, attr); - } else if (isupper (name [0]) || m_first->is_const ()) { if ((mt->end (mid) - mt->begin (mid)) == 1 && m_first->begin_arguments () == m_first->end_arguments ()) { @@ -2782,7 +2775,7 @@ public: mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This attribute is not available for Python"))); } - } else { + } else if (! as_static) { if (m_first->ret_type ().type () == gsi::T_object && m_first->ret_type ().pass_obj () && name == "new") { @@ -2813,104 +2806,106 @@ public: } - } + if (! as_static) { - // Complete the comparison operators if necessary. - // Unlike Ruby, Python does not automatically implement != from == for example. - // We assume that "==" and "<" are the minimum requirements for full comparison - // and "==" is the minimum requirement for equality. Hence: - // * If "==" is given, but no "!=", synthesize - // "a != b" by "!a == b" - // * If "==" and "<" are given, synthesize if required - // "a <= b" by "a < b || a == b" - // "a > b" by "!(a < b || a == b)" (could be b < a, but this avoids having to switch arguments) - // "a >= b" by "!a < b" + // Complete the comparison operators if necessary. + // Unlike Ruby, Python does not automatically implement != from == for example. + // We assume that "==" and "<" are the minimum requirements for full comparison + // and "==" is the minimum requirement for equality. Hence: + // * If "==" is given, but no "!=", synthesize + // "a != b" by "!a == b" + // * If "==" and "<" are given, synthesize if required + // "a <= b" by "a < b || a == b" + // "a > b" by "!(a < b || a == b)" (could be b < a, but this avoids having to switch arguments) + // "a >= b" by "!a < b" - bool has_eq = mt->find_method (false, "==").first; - bool has_ne = mt->find_method (false, "!=").first; - bool has_lt = mt->find_method (false, "<").first; - bool has_le = mt->find_method (false, "<=").first; - bool has_gt = mt->find_method (false, ">").first; - bool has_ge = mt->find_method (false, ">=").first; - bool has_cmp = mt->find_method (false, "<=>").first; + bool has_eq = mt->find_method (false, "==").first; + bool has_ne = mt->find_method (false, "!=").first; + bool has_lt = mt->find_method (false, "<").first; + bool has_le = mt->find_method (false, "<=").first; + bool has_gt = mt->find_method (false, ">").first; + bool has_ge = mt->find_method (false, ">=").first; + bool has_cmp = mt->find_method (false, "<=>").first; - if (! has_cmp && has_eq) { + if (! has_cmp && has_eq) { - if (! has_ne) { + if (! has_ne) { - // Add a definition for "__ne__" - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__ne__"; - method->ml_meth = &object_default_ne_impl; - method->ml_flags = METH_VARARGS; + // Add a definition for "__ne__" + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = "__ne__"; + method->ml_meth = &object_default_ne_impl; + method->ml_flags = METH_VARARGS; - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); + set_type_attr (type, method->ml_name, attr); + + } + + if (has_lt && ! has_le) { + + // Add a definition for "__le__" + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = "__le__"; + method->ml_meth = &object_default_le_impl; + method->ml_flags = METH_VARARGS; + + PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); + set_type_attr (type, method->ml_name, attr); + + } + + if (has_lt && ! has_gt) { + + // Add a definition for "__gt__" + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = "__gt__"; + method->ml_meth = &object_default_gt_impl; + method->ml_flags = METH_VARARGS; + + PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); + set_type_attr (type, method->ml_name, attr); + + } + + if (has_lt && ! has_ge) { + + // Add a definition for "__ge__" + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = "__ge__"; + method->ml_meth = &object_default_ge_impl; + method->ml_flags = METH_VARARGS; + + PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); + set_type_attr (type, method->ml_name, attr); + + } + + } } - if (has_lt && ! has_le) { + // install the static/non-static dispatcher descriptor - // Add a definition for "__le__" - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__le__"; - method->ml_meth = &object_default_le_impl; - method->ml_flags = METH_VARARGS; + for (std::vector::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) { - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + PyObject *attr_inst = PyObject_GetAttrString ((PyObject *) type, ("_inst_" + *a).c_str ()); + PyObject *attr_class = PyObject_GetAttrString ((PyObject *) type, ("_class_" + *a).c_str ()); + if (attr_inst == NULL || attr_class == NULL) { - } + // some error or disambiguator is not required -> don't install it + Py_XDECREF (attr_inst); + Py_XDECREF (attr_class); + PyErr_Clear (); - if (has_lt && ! has_gt) { + } else { - // Add a definition for "__gt__" - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__gt__"; - method->ml_meth = &object_default_gt_impl; - method->ml_flags = METH_VARARGS; + PyObject *desc = PYAAmbiguousMethodDispatcher::create (attr_inst, attr_class); + PythonRef name (c2python (*a)); + // Note: we use GenericSetAttr since that one allows us setting attributes on built-in types + PyObject_GenericSetAttr ((PyObject *) type, name.get (), desc); - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); - - } - - if (has_lt && ! has_ge) { - - // Add a definition for "__ge__" - PyMethodDef *method = mp_module->make_method_def (); - method->ml_name = "__ge__"; - method->ml_meth = &object_default_ge_impl; - method->ml_flags = METH_VARARGS; - - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); - - } - - } - - // install the static/non-static dispatcher descriptor - - for (std::vector::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) { - - PyObject *attr_inst = PyObject_GetAttrString ((PyObject *) type, ("_inst_" + *a).c_str ()); - PyObject *attr_class = PyObject_GetAttrString ((PyObject *) type, ("_class_" + *a).c_str ()); - if (attr_inst == NULL || attr_class == NULL) { - - // some error -> don't install the disambiguator - Py_XDECREF (attr_inst); - Py_XDECREF (attr_class); - PyErr_Clear (); - - tl::warn << "Unable to install a static/non-static disambiguator for " << *a << " in class " << cls->name (); - - } else { - - PyObject *desc = PYAAmbiguousMethodDispatcher::create (attr_inst, attr_class); - PythonRef name (c2python (*a)); - // Note: we use GenericSetAttr since that one allows us setting attributes on built-in types - PyObject_GenericSetAttr ((PyObject *) type, name.get (), desc); + } } @@ -2925,7 +2920,6 @@ private: PythonModule *mp_module; PyObject *m_all_list; std::map > m_extensions_for; - std::set m_extensions; std::map > > m_links_for; }; @@ -2968,16 +2962,16 @@ PythonModule::make_classes (const char *mod_name) for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { if ((*c)->module () != mod_name) { // don't handle classes outside this module, but require them to be present - if (! PythonClassClientData::py_type (**c)) { + if (! PythonClassClientData::py_type (**c, false)) { throw tl::Exception (tl::sprintf ("class %s.%s required from outside the module %s, but that module is not loaded", (*c)->module (), (*c)->name (), mod_name)); } } } } - // first pass: register the extensions - for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { - if ((*c)->declaration () != *c && (! mod_name || (*c)->module () == mod_name)) { + // first pass: register the extensions using all available classes + for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { + if ((*c)->declaration () != *c) { gen.register_extension (*c); } } @@ -2985,7 +2979,7 @@ PythonModule::make_classes (const char *mod_name) // second pass: make the classes for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { if ((*c)->declaration () == *c && (! mod_name || (*c)->module () == mod_name)) { - gen.make_class (*c); + gen.make_class (*c, false); } } } @@ -3010,7 +3004,7 @@ const gsi::ClassBase *PythonModule::cls_for_type (PyTypeObject *type) PyTypeObject *PythonModule::type_for_cls (const gsi::ClassBase *cls) { - return PythonClassClientData::py_type (*cls); + return PythonClassClientData::py_type (*cls, false); } } diff --git a/src/pya/pya/pyaModule.h b/src/pya/pya/pyaModule.h index 1656a11e4..af21dbe48 100644 --- a/src/pya/pya/pyaModule.h +++ b/src/pya/pya/pyaModule.h @@ -158,8 +158,6 @@ public: } private: - static void check (const char *mod_name); - std::list m_string_heap; std::vector m_methods_heap; std::vector m_getseters_heap; diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index 551e61c57..74618fef1 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -586,5 +586,17 @@ PYAObjectBase::obj () return m_obj; } +PYAObjectBase * +PYAObjectBase::from_pyobject (PyObject *py_object) +{ + if (Py_TYPE (py_object)->tp_init == NULL) { + throw tl::Exception (tl::to_string (tr ("Extension classes do not support instance methods or properties"))); + } + + PYAObjectBase *pya_object = from_pyobject_unsafe (py_object); + tl_assert (pya_object->py_object () == py_object); + return pya_object; +} + } diff --git a/src/pya/pya/pyaObject.h b/src/pya/pya/pyaObject.h index 36e1ab97d..fbf183848 100644 --- a/src/pya/pya/pyaObject.h +++ b/src/pya/pya/pyaObject.h @@ -70,25 +70,21 @@ public: */ ~PYAObjectBase (); - /** - * @brief Gets the PYAObjectBase pointer from a PyObject pointer - */ - static PYAObjectBase *from_pyobject (PyObject *py_object) - { - PYAObjectBase *pya_object = (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase)); - tl_assert (pya_object->py_object () == py_object); - return pya_object; - } - /** * @brief Gets the PYAObjectBase pointer from a PyObject pointer * This version doesn't check anything. */ static PYAObjectBase *from_pyobject_unsafe (PyObject *py_object) { + // the objects must not be a pure static extension return (PYAObjectBase *)((char *) py_object + Py_TYPE (py_object)->tp_basicsize - sizeof (PYAObjectBase)); } + /** + * @brief Gets the PYAObjectBase pointer from a PyObject pointer + */ + static PYAObjectBase *from_pyobject (PyObject *py_object); + /** * @brief Indicates that a C++ object is present */ diff --git a/testdata/python/basic.py b/testdata/python/basic.py index 5bd49cb16..ea1e8c6c9 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -3045,8 +3045,23 @@ class BasicTest(unittest.TestCase): self.assertEqual(pya.A.ba_to_ia(b'\x00\x01\x02'), [ 0, 1, 2 ]) + # Tests multi-base mixins (only constants and enums available) + def test_multiBaseMixins(self): + + bb = pya.BB() # base classes B1,B2,B3 + bb.set1(17) # B1 + self.assertEqual(bb.get1(), 17) # B1 + bb.set1(21) # B1 + + self.assertEqual(bb.get1(), 21) # B1 + self.assertEqual(pya.BB.C2, 17) # B2 + self.assertEqual(pya.BB.C3, -1) # B3 + self.assertEqual(pya.BB.E.E3B.to_i(), 101) # B3 + self.assertEqual(bb.d3(pya.BB.E.E3C, pya.BB.E.E3A), -2) # BB with B3 enums + self.assertEqual(bb.d3(pya.BB.E.E3A, pya.BB.E.E3C), 2) # BB with B3 enums + # Custom factory implemented in Python -# + def test_80(self): gc = pya.GObject.g_inst_count() From 0aca56b1c67d188042622bfa5295deae247fca04 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 10 Dec 2021 01:23:54 +0100 Subject: [PATCH 05/17] Fixed pymod tests --- src/gsiqt/qt6/QtCore5Compat/QtCore5Compat.pro | 2 + src/pymod/unit_tests/pymod_tests.cc | 20 +++++- testdata/pymod/import_QtCore5Compat.py | 43 ++++++++++++ testdata/pymod/import_QtGui_Qt6.py | 67 +++++++++++++++++++ testdata/pymod/import_QtSvg_Qt6.py | 45 +++++++++++++ testdata/pymod/import_QtWidgets_Qt6.py | 48 +++++++++++++ 6 files changed, 223 insertions(+), 2 deletions(-) create mode 100755 testdata/pymod/import_QtCore5Compat.py create mode 100755 testdata/pymod/import_QtGui_Qt6.py create mode 100755 testdata/pymod/import_QtSvg_Qt6.py create mode 100755 testdata/pymod/import_QtWidgets_Qt6.py diff --git a/src/gsiqt/qt6/QtCore5Compat/QtCore5Compat.pro b/src/gsiqt/qt6/QtCore5Compat/QtCore5Compat.pro index b7ba362d6..1189368a9 100644 --- a/src/gsiqt/qt6/QtCore5Compat/QtCore5Compat.pro +++ b/src/gsiqt/qt6/QtCore5Compat/QtCore5Compat.pro @@ -13,6 +13,8 @@ DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC $$QTBASIC_INC LIBS += -L$$DESTDIR -lklayout_tl -lklayout_gsi -lklayout_qtbasic +LIBS += -lklayout_QtCore + SOURCES += \ HEADERS += \ diff --git a/src/pymod/unit_tests/pymod_tests.cc b/src/pymod/unit_tests/pymod_tests.cc index 83fdd0c5c..a04e3c9ad 100644 --- a/src/pymod/unit_tests/pymod_tests.cc +++ b/src/pymod/unit_tests/pymod_tests.cc @@ -93,7 +93,11 @@ PYMODTEST (import_rdb, "import_rdb.py") PYMODTEST (import_lay, "import_lay.py") PYMODTEST (import_QtCore, "import_QtCore.py") +#if QT_VERSION >= 0x60000 +PYMODTEST (import_QtGui, "import_QtGui_Qt6.py") +#else PYMODTEST (import_QtGui, "import_QtGui.py") +#endif #if defined(HAVE_QT_XML) PYMODTEST (import_QtXml, "import_QtXml.py") #endif @@ -103,7 +107,7 @@ PYMODTEST (import_QtSql, "import_QtSql.py") #if defined(HAVE_QT_NETWORK) PYMODTEST (import_QtNetwork, "import_QtNetwork.py") #endif -#if defined(HAVE_QT_DESIGNER) +#if defined(HAVE_QT_DESIGNER) && QT_VERSION < 0x60000 PYMODTEST (import_QtDesigner, "import_QtDesigner.py") #endif #if defined(HAVE_QT_UITOOLS) @@ -112,7 +116,11 @@ PYMODTEST (import_QtUiTools, "import_QtUiTools.py") #if QT_VERSION >= 0x50000 +#if QT_VERSION >= 0x60000 +PYMODTEST (import_QtWidgets, "import_QtWidgets_Qt6.py") +#else PYMODTEST (import_QtWidgets, "import_QtWidgets.py") +#endif #if defined(HAVE_QT_MULTIMEDIA) PYMODTEST (import_QtMultimedia, "import_QtMultimedia.py") #endif @@ -120,12 +128,20 @@ PYMODTEST (import_QtMultimedia, "import_QtMultimedia.py") PYMODTEST (import_QtPrintSupport, "import_QtPrintSupport.py") #endif #if defined(HAVE_QT_SVG) +#if QT_VERSION >= 0x60000 +PYMODTEST (import_QtSvg, "import_QtSvg_Qt6.py") +#else PYMODTEST (import_QtSvg, "import_QtSvg.py") #endif -#if defined(HAVE_QT_XML) +#endif +#if defined(HAVE_QT_XML) && QT_VERSION < 0x60000 PYMODTEST (import_QtXmlPatterns, "import_QtXmlPatterns.py") #endif +#if QT_VERSION >= 0x50000 +PYMODTEST (import_QtCore5Compat, "import_QtCore5Compat.py") +#endif + #endif PYMODTEST (import_pya, "pya_tests.py") diff --git a/testdata/pymod/import_QtCore5Compat.py b/testdata/pymod/import_QtCore5Compat.py new file mode 100755 index 000000000..de534eccd --- /dev/null +++ b/testdata/pymod/import_QtCore5Compat.py @@ -0,0 +1,43 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2021 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import testprep +import klayout.QtCore5Compat as QtCore5Compat +import unittest +import sys + +# Tests the basic abilities of the module + +class BasicTest(unittest.TestCase): + + def test_1(self): + self.assertEqual("QRegExp" in QtCore5Compat.__all__, True) + + def test_2(self): + # Some smoke test + re = QtCore5Compat.QRegExp("a.*b") + self.assertEqual(re.indexIn("xauby"), 1) + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestSuite() + suite = unittest.TestLoader().loadTestsFromTestCase(BasicTest) + + if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) + + diff --git a/testdata/pymod/import_QtGui_Qt6.py b/testdata/pymod/import_QtGui_Qt6.py new file mode 100755 index 000000000..b8aee10bf --- /dev/null +++ b/testdata/pymod/import_QtGui_Qt6.py @@ -0,0 +1,67 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2021 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import testprep +import klayout.QtCore as QtCore +import klayout.QtWidgets as QtWidgets +import klayout.QtGui as QtGui +import unittest +import sys + +# Tests the basic abilities of the module + +class BasicTest(unittest.TestCase): + + def test_1(self): + self.assertEqual("QColor" in QtGui.__all__, True) + self.assertEqual("QAction" in QtGui.__all__, True) + + def test_2(self): + # Some smoke test + v = QtGui.QColor(12, 34, 68) + self.assertEqual(v.red, 12) + v.red = 17 + self.assertEqual(v.red, 17) + self.assertEqual(v.green, 34) + self.assertEqual(v.blue, 68) + + trg = 0 + + def onTrigger(self): + self.trg += 17 + + def test_3(self): + app = QtWidgets.QApplication([ "progname" ]) + a = QtGui.QAction(None) + a.text = "myaction" + self.assertEqual(a.text, "myaction") + self.trg = 0 + a.triggered = self.onTrigger + a.trigger() + self.assertEqual(self.trg, 17) + a.trigger() + self.assertEqual(self.trg, 34) + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestSuite() + suite = unittest.TestLoader().loadTestsFromTestCase(BasicTest) + + if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) + + diff --git a/testdata/pymod/import_QtSvg_Qt6.py b/testdata/pymod/import_QtSvg_Qt6.py new file mode 100755 index 000000000..299ad1d89 --- /dev/null +++ b/testdata/pymod/import_QtSvg_Qt6.py @@ -0,0 +1,45 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2021 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import testprep +import klayout.QtCore as QtCore +import klayout.QtGui as QtGui +import klayout.QtWidgets as QtWidgets +import klayout.QtSvg as QtSvg +import unittest +import sys + +# Tests the basic abilities of the module + +class BasicTest(unittest.TestCase): + + def test_1(self): + self.assertEqual("QSvgRenderer" in QtSvg.__all__, True) + + def test_2(self): + # Smoke test + q = QtSvg.QSvgRenderer() + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestSuite() + suite = unittest.TestLoader().loadTestsFromTestCase(BasicTest) + + if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) + + diff --git a/testdata/pymod/import_QtWidgets_Qt6.py b/testdata/pymod/import_QtWidgets_Qt6.py new file mode 100755 index 000000000..9f53a9e7b --- /dev/null +++ b/testdata/pymod/import_QtWidgets_Qt6.py @@ -0,0 +1,48 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2021 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import testprep +import klayout.QtCore as QtCore +import klayout.QtGui as QtGui +import klayout.QtWidgets as QtWidgets +import unittest +import sys + +# Tests the basic abilities of the module + +class BasicTest(unittest.TestCase): + + def test_1(self): + self.assertEqual("QGraphicsLineItem" in QtWidgets.__all__, True) + + def test_2(self): + i = QtWidgets.QGraphicsLineItem() + i.line = QtCore.QLineF(1, 2, 3, 4) + self.assertEqual(i.line.x1(), 1) + self.assertEqual(i.line.y1(), 2) + self.assertEqual(i.line.x2(), 3) + self.assertEqual(i.line.y2(), 4) + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestSuite() + suite = unittest.TestLoader().loadTestsFromTestCase(BasicTest) + + if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) + + From ca304b15c8f9d9b2b436985e801617e7a0b42419 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 00:14:10 +0100 Subject: [PATCH 06/17] Enable multi-base support for RBA + Qt binding --- src/pya/pya/pyaModule.cc | 8 ++-- src/rba/rba/rba.cc | 71 ++++++++++++++++++--------------- src/rba/rba/rbaConvert.cc | 4 +- src/rba/rba/rbaInternal.cc | 14 +++---- src/rba/rba/rbaInternal.h | 6 +-- testdata/ruby/basic_testcore.rb | 1 + 6 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 6aed6692c..4ff62f83f 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -2488,9 +2488,11 @@ public: // produce the child classes for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { - PyTypeObject *child_class = make_class (cc.operator-> (), as_static); - PythonRef attr ((PyObject *) child_class, false /*borrowed*/); - set_type_attr (type, cc->name ().c_str (), attr); + if (cc->declaration () == cc.operator-> ()) { + PyTypeObject *child_class = make_class (cc.operator-> (), as_static); + PythonRef attr ((PyObject *) child_class, false /*borrowed*/); + set_type_attr (type, cc->name ().c_str (), attr); + } } // add named extensions diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index aafe5e881..e17c57ad6 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1541,7 +1541,6 @@ public: // got an extension tl_assert (cls->parent ()); m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); - m_extensions.insert (cls->declaration ()); } else { @@ -1550,76 +1549,84 @@ public: } } - VALUE make_class (const gsi::ClassBase *cls) + VALUE make_class (const gsi::ClassBase *cls, bool as_static, VALUE parent_class = (VALUE) 0, const gsi::ClassBase *parent = 0) { - if (is_registered (cls)) { - return ruby_cls (cls); + if (is_registered (cls, as_static)) { + return ruby_cls (cls, as_static); } - 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 (cls->base () != 0) { - super = make_class (cls->base ()); + super = make_class (cls->base (), as_static); } +if (cls->name() == "ViewMode") {// @@@ + printf("@@@ BANG!\n"); fflush(stdout); +}// @@@ VALUE klass; - if (is_extension) { + if (as_static) { 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 (); + + std::string mixin_name = cls->name () + "_Mixin"; + + if (parent) { + klass = rb_define_module_under (parent_class, mixin_name.c_str ()); + } else { + klass = rb_define_module_under (m_module, mixin_name.c_str ()); } - 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 ()); + // if the base class is an extension (mixin), we cannot use it as superclass because it's a module + if (cls->base () != 0) { + rb_include_module (klass, super); } } else { - 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); + if (parent) { + klass = rb_define_class_under (parent_class, cls->name ().c_str (), super); } else { - klass = rb_define_class_under (m_module, cls->name ().c_str (), direct_super); + klass = rb_define_class_under (m_module, cls->name ().c_str (), super); } rb_define_alloc_func (klass, alloc_proxy); } - // 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); - } - - register_class (klass, cls); + register_class (klass, cls, as_static); // mix-in unnamed extensions 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); + VALUE ext_module = make_class (*ie, true); rb_include_module (klass, ext_module); rb_extend_object (klass, ext_module); } } + // produce the child classes + + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + if (cc->declaration () == cc.operator-> ()) { + if (! is_registered (cc->declaration (), false)) { + make_class (cc->declaration (), false, klass, cc->declaration ()); + } else { + VALUE child_class = ruby_cls (cc->declaration (), false); + rb_define_const (klass, cc->name ().c_str (), child_class); + } + } + } + // 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); + VALUE linked_class = make_class (il->second, false); rb_define_const (klass, il->first.c_str (), linked_class); } } @@ -1702,7 +1709,7 @@ public: // 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 (! as_static) { // Hint: we need to do static methods before the non-static ones because // rb_define_module_function creates an private instance method. @@ -1838,7 +1845,7 @@ rba_init (RubyInterpreterPrivateData *d) // second pass: make the classes for (auto c = sorted_classes.begin (); c != sorted_classes.end (); ++c) { if ((*c)->declaration () == *c) { - gen.make_class (*c); + gen.make_class (*c, false); } } diff --git a/src/rba/rba/rbaConvert.cc b/src/rba/rba/rbaConvert.cc index da7304264..919a52e9e 100644 --- a/src/rba/rba/rbaConvert.cc +++ b/src/rba/rba/rbaConvert.cc @@ -213,7 +213,7 @@ object_to_ruby (void *obj, Proxy *self, const gsi::ClassBase *cls, bool pass_obj // of the exposed property. Hence copying is safer. // create a instance and copy the value - ret = rb_obj_alloc (ruby_cls (clsact)); + ret = rb_obj_alloc (ruby_cls (clsact, false)); Proxy *p = 0; Data_Get_Struct (ret, Proxy, p); clsact->assign (p->obj (), obj); @@ -243,7 +243,7 @@ object_to_ruby (void *obj, Proxy *self, const gsi::ClassBase *cls, bool pass_obj // TODO: we will create a fresh object here, delete it again and link the // reference to the existing object to the Ruby object. This is not quite // efficient - we should avoid creating and deleting a dummy object first. - ret = rb_obj_alloc (ruby_cls (clsact)); + ret = rb_obj_alloc (ruby_cls (clsact, false)); Proxy *p = 0; Data_Get_Struct (ret, Proxy, p); p->set (obj, pass_obj, is_const /*const*/, can_destroy /*can_destroy*/, ret); diff --git a/src/rba/rba/rbaInternal.cc b/src/rba/rba/rbaInternal.cc index a7a1ecaf5..d752a2493 100644 --- a/src/rba/rba/rbaInternal.cc +++ b/src/rba/rba/rbaInternal.cc @@ -919,24 +919,24 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs // Class map management static std::map cls_map; -static std::map rev_cls_map; +static std::map , VALUE> rev_cls_map; -void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls) +void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls, bool as_static) { cls_map.insert (std::make_pair (ruby_cls, gsi_cls)); - rev_cls_map.insert (std::make_pair (gsi_cls, ruby_cls)); + rev_cls_map.insert (std::make_pair (std::make_pair (gsi_cls, as_static), ruby_cls)); } -VALUE ruby_cls (const gsi::ClassBase *cls) +VALUE ruby_cls (const gsi::ClassBase *cls, bool as_static) { - std::map ::const_iterator c = rev_cls_map.find (cls); + auto c = rev_cls_map.find (std::make_pair (cls, as_static)); tl_assert (c != rev_cls_map.end ()); return c->second; } -bool is_registered (const gsi::ClassBase *cls) +bool is_registered (const gsi::ClassBase *cls, bool as_static) { - return rev_cls_map.find (cls) != rev_cls_map.end (); + return rev_cls_map.find (std::make_pair (cls, as_static)) != rev_cls_map.end (); } const gsi::ClassBase *find_cclass (VALUE k) diff --git a/src/rba/rba/rbaInternal.h b/src/rba/rba/rbaInternal.h index 73a1a0011..b5b36948c 100644 --- a/src/rba/rba/rbaInternal.h +++ b/src/rba/rba/rbaInternal.h @@ -211,7 +211,7 @@ private: /** * @brief Registers a Ruby class for a gsi class */ -void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls); +void register_class (VALUE ruby_cls, const gsi::ClassBase *gsi_cls, bool as_static); /** * @brief Find the class declaration from the Ruby object @@ -226,12 +226,12 @@ const gsi::ClassBase *find_cclass_maybe_null (VALUE k); /** * @brief Finds the Ruby class for a gsi class */ -VALUE ruby_cls (const gsi::ClassBase *cls); +VALUE ruby_cls (const gsi::ClassBase *cls, bool as_static); /** * @brief Gets a value indicating whether a Ruby class is registered for a GSI class */ -bool is_registered (const gsi::ClassBase *gsi_cls); +bool is_registered (const gsi::ClassBase *gsi_cls, bool as_static); /** * @brief Locks the Ruby object against destruction by the GC diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 65344ce41..de8e911b3 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -3019,6 +3019,7 @@ class Basic_TestClass < TestBase bb.set1(17) # B1 assert_equal(bb.get1, 17) # B1 bb.set1(21) # B1 + assert_equal(RBA::B3::E::E3B.to_i, 101) # B3 assert_equal(bb.get1, 21) # B1 assert_equal(RBA::BB::C2, 17) # B2 From 8dbc31a41f1cdec01bc4b357d47c66daf64ec5f4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 00:42:26 +0100 Subject: [PATCH 07/17] Removed debug output --- src/rba/rba/rba.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index e17c57ad6..89bd04321 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1560,9 +1560,6 @@ public: super = make_class (cls->base (), as_static); } -if (cls->name() == "ViewMode") {// @@@ - printf("@@@ BANG!\n"); fflush(stdout); -}// @@@ VALUE klass; if (as_static) { From 2d334bd88dadca015af6d13485a4a37670fe527d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 01:09:07 +0100 Subject: [PATCH 08/17] Some code simplification --- src/pya/pya/pyaModule.cc | 20 +------------------- src/rba/rba/rba.cc | 19 +------------------ 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 4ff62f83f..19318550b 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -2387,15 +2387,9 @@ public: void register_extension (const gsi::ClassBase *cls) { if (cls->name ().empty ()) { - // got an extension tl_assert (cls->parent ()); m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); - - } else { - - m_links_for [cls->parent ()->declaration ()].push_back (std::make_pair (cls->name (), cls->declaration ())); - } } @@ -2488,24 +2482,13 @@ public: // produce the child classes for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { - if (cc->declaration () == cc.operator-> ()) { + if (! cc->name ().empty ()) { PyTypeObject *child_class = make_class (cc.operator-> (), as_static); PythonRef attr ((PyObject *) child_class, false /*borrowed*/); set_type_attr (type, cc->name ().c_str (), attr); } } - // 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) { - PyTypeObject *linked_type = make_class (il->second, false); - PythonRef attr ((PyObject *) linked_type, false /*borrowed*/); - set_type_attr (type, il->first.c_str (), attr); - } - } - // Build the attributes now ... MethodTable *mt = MethodTable::method_table_by_class (cls); @@ -2922,7 +2905,6 @@ private: PythonModule *mp_module; PyObject *m_all_list; std::map > m_extensions_for; - std::map > > m_links_for; }; } diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 89bd04321..ab7d56a09 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1537,15 +1537,9 @@ public: void register_extension (const gsi::ClassBase *cls) { if (cls->name ().empty ()) { - // got an extension tl_assert (cls->parent ()); m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); - - } else { - - m_links_for [cls->parent ()->declaration ()].push_back (std::make_pair (cls->name (), cls->declaration ())); - } } @@ -1608,7 +1602,7 @@ public: // produce the child classes for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { - if (cc->declaration () == cc.operator-> ()) { + if (! cc->name ().empty ()) { if (! is_registered (cc->declaration (), false)) { make_class (cc->declaration (), false, klass, cc->declaration ()); } else { @@ -1618,16 +1612,6 @@ public: } } - // 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, false); - 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) { @@ -1811,7 +1795,6 @@ private: std::vector m_constants; std::map > m_extensions_for; std::set m_extensions; - std::map > > m_links_for; }; } From 93942e907efd6ec56120b45e77330b87a87aaa60 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 10:14:14 +0100 Subject: [PATCH 09/17] Updated Qt5 bindings --- scripts/mkqtdecl_common/produce.rb | 4 +- src/gsiqt/qt5/QtCore/QtCore.pri | 3 +- src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc | 56 +++++++++++++++++++ .../qt5/QtGui/gsiDeclQOffscreenSurface.cc | 6 ++ .../qt5/QtGui/gsiDeclQPaintDeviceWindow.cc | 6 ++ src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc | 6 ++ src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc | 6 ++ .../QtMultimedia/gsiDeclQAudioSystemPlugin.cc | 6 ++ .../gsiDeclQCameraImageCapture.cc | 6 ++ .../QtMultimedia/gsiDeclQGraphicsVideoItem.cc | 6 ++ .../qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc | 6 ++ .../qt5/QtMultimedia/gsiDeclQMediaRecorder.cc | 6 ++ .../gsiDeclQMediaServiceProviderPlugin.cc | 6 ++ .../qt5/QtMultimedia/gsiDeclQRadioData.cc | 6 ++ .../qt5/QtMultimedia/gsiDeclQVideoWidget.cc | 6 ++ .../qt5/QtWidgets/gsiDeclQAccessibleWidget.cc | 6 ++ .../qt5/QtWidgets/gsiDeclQGraphicsObject.cc | 6 ++ .../qt5/QtWidgets/gsiDeclQGraphicsWidget.cc | 6 ++ src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc | 6 ++ src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc | 6 ++ .../qt5/QtXml/gsiDeclQXmlDefaultHandler.cc | 14 +++++ 21 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc diff --git a/scripts/mkqtdecl_common/produce.rb b/scripts/mkqtdecl_common/produce.rb index 59fb97f6c..47fa3057f 100755 --- a/scripts/mkqtdecl_common/produce.rb +++ b/scripts/mkqtdecl_common/produce.rb @@ -1107,9 +1107,9 @@ class Configurator else dc = (@dropped_classes[:all_classes] || []) + (@dropped_classes[cls] || []) if sig != :whole_class - return dc.find { |d| sig =~ d } != nil + return dc.find { |d| d == :whole_class || sig =~ d } != nil else - return dc.find { |d| sig == d } != nil + return dc.find { |d| d == :whole_class || sig == d } != nil end end end diff --git a/src/gsiqt/qt5/QtCore/QtCore.pri b/src/gsiqt/qt5/QtCore/QtCore.pri index 04b0ffc6b..080d8c605 100644 --- a/src/gsiqt/qt5/QtCore/QtCore.pri +++ b/src/gsiqt/qt5/QtCore/QtCore.pri @@ -179,7 +179,8 @@ SOURCES += \ $$PWD/gsiDeclQt_1.cc \ $$PWD/gsiDeclQt_2.cc \ $$PWD/gsiDeclQt_3.cc \ - $$PWD/gsiDeclQt_4.cc + $$PWD/gsiDeclQt_4.cc \ + $$PWD/gsiDeclQtMsgType.cc HEADERS += gsiQtCoreCommon.h diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc b/src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc new file mode 100644 index 000000000..0f13177df --- /dev/null +++ b/src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc @@ -0,0 +1,56 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2021 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** +* @file gsiDeclQtMsgType.cc +* +* DO NOT EDIT THIS FILE. +* This file has been created automatically +*/ + +#include +#include "gsiQt.h" +#include "gsiQtCoreCommon.h" +#include + +// ----------------------------------------------------------------------- +// enum QtMsgType + + +// Implementation of the enum wrapper class for ::QtMsgType +namespace qt_gsi +{ + +static gsi::Enum decl_QtMsgType_Enum ("QtCore", "QtMsgType", + gsi::enum_const ("QtDebugMsg", QtDebugMsg, "@brief Enum constant QtDebugMsg") + + gsi::enum_const ("QtWarningMsg", QtWarningMsg, "@brief Enum constant QtWarningMsg") + + gsi::enum_const ("QtCriticalMsg", QtCriticalMsg, "@brief Enum constant QtCriticalMsg") + + gsi::enum_const ("QtFatalMsg", QtFatalMsg, "@brief Enum constant QtFatalMsg") + + gsi::enum_const ("QtInfoMsg", QtInfoMsg, "@brief Enum constant QtInfoMsg") + + gsi::enum_const ("QtSystemMsg", QtSystemMsg, "@brief Enum constant QtSystemMsg"), + "@qt\n@brief This class represents the QtMsgType enum"); + +static gsi::QFlagsClass decl_QtMsgType_Enums ("QtCore", "QFlags_QtMsgType", + "@qt\n@brief This class represents the QFlags flag set"); + +} + diff --git a/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc b/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc index d12f95d76..cc6eaff5a 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQOffscreenSurface.cc @@ -340,6 +340,12 @@ qt_gsi::QtNativeClass decl_QOffscreenSurface (qtdecl_QObject 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/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc index 7ceb8e117..624655baf 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPaintDeviceWindow.cc @@ -322,6 +322,12 @@ qt_gsi::QtNativeClass decl_QPaintDeviceWindow (qtdecl_QWindo 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/qt5/QtGui/gsiDeclQPdfWriter.cc b/src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc index 9d915767c..a77d54f8d 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQPdfWriter.cc @@ -370,6 +370,12 @@ qt_gsi::QtNativeClass decl_QPdfWriter (qtdecl_QObject (), "QtGui", " 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/qt5/QtGui/gsiDeclQWindow.cc b/src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc index 37ee80f3e..82649421f 100644 --- a/src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc +++ b/src/gsiqt/qt5/QtGui/gsiDeclQWindow.cc @@ -2058,6 +2058,12 @@ qt_gsi::QtNativeClass decl_QWindow (qtdecl_QObject (), "QtGui", "QWindo 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/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc index 935cc5b7f..55dddfd18 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQAudioSystemPlugin.cc @@ -256,6 +256,12 @@ qt_gsi::QtNativeClass decl_QAudioSystemPlugin (qtdecl_QObjec methods_QAudioSystemPlugin (), "@hide\n@alias QAudioSystemPlugin"); +// Additional base classes + +gsi::Class &qtdecl_QAudioSystemFactoryInterface (); + +gsi::ClassExt base_class_QAudioSystemFactoryInterface_in_QAudioSystemPlugin (qtdecl_QAudioSystemFactoryInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QAudioSystemPlugin () { return decl_QAudioSystemPlugin; } } diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc index 8a54f68f1..2273b5267 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQCameraImageCapture.cc @@ -724,6 +724,12 @@ qt_gsi::QtNativeClass decl_QCameraImageCapture (qtdecl_QObj methods_QCameraImageCapture (), "@hide\n@alias QCameraImageCapture"); +// Additional base classes + +gsi::Class &qtdecl_QMediaBindableInterface (); + +gsi::ClassExt base_class_QMediaBindableInterface_in_QCameraImageCapture (qtdecl_QMediaBindableInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QCameraImageCapture () { return decl_QCameraImageCapture; } } diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc index 4ecee9b78..701946dcb 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQGraphicsVideoItem.cc @@ -405,6 +405,12 @@ qt_gsi::QtNativeClass decl_QGraphicsVideoItem (qtdecl_QGraph methods_QGraphicsVideoItem (), "@hide\n@alias QGraphicsVideoItem"); +// Additional base classes + +gsi::Class &qtdecl_QMediaBindableInterface (); + +gsi::ClassExt base_class_QMediaBindableInterface_in_QGraphicsVideoItem (qtdecl_QMediaBindableInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QGraphicsVideoItem () { return decl_QGraphicsVideoItem; } } diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc index a07bccc33..3af70a850 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaPlaylist.cc @@ -952,6 +952,12 @@ qt_gsi::QtNativeClass decl_QMediaPlaylist (qtdecl_QObject (), "Q methods_QMediaPlaylist (), "@hide\n@alias QMediaPlaylist"); +// Additional base classes + +gsi::Class &qtdecl_QMediaBindableInterface (); + +gsi::ClassExt base_class_QMediaBindableInterface_in_QMediaPlaylist (qtdecl_QMediaBindableInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QMediaPlaylist () { return decl_QMediaPlaylist; } } diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc index d1e1dacb5..a74bd78e9 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaRecorder.cc @@ -1159,6 +1159,12 @@ qt_gsi::QtNativeClass decl_QMediaRecorder (qtdecl_QObject (), "Q methods_QMediaRecorder (), "@hide\n@alias QMediaRecorder"); +// Additional base classes + +gsi::Class &qtdecl_QMediaBindableInterface (); + +gsi::ClassExt base_class_QMediaBindableInterface_in_QMediaRecorder (qtdecl_QMediaBindableInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QMediaRecorder () { return decl_QMediaRecorder; } } diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc index e06ce2509..77bb61c1c 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQMediaServiceProviderPlugin.cc @@ -212,6 +212,12 @@ qt_gsi::QtNativeClass decl_QMediaServiceProviderPlu methods_QMediaServiceProviderPlugin (), "@hide\n@alias QMediaServiceProviderPlugin"); +// Additional base classes + +gsi::Class &qtdecl_QMediaServiceProviderFactoryInterface (); + +gsi::ClassExt base_class_QMediaServiceProviderFactoryInterface_in_QMediaServiceProviderPlugin (qtdecl_QMediaServiceProviderFactoryInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QMediaServiceProviderPlugin () { return decl_QMediaServiceProviderPlugin; } } diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc index d836c4a96..f6c3a7026 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQRadioData.cc @@ -499,6 +499,12 @@ qt_gsi::QtNativeClass decl_QRadioData (qtdecl_QObject (), "QtMultime methods_QRadioData (), "@hide\n@alias QRadioData"); +// Additional base classes + +gsi::Class &qtdecl_QMediaBindableInterface (); + +gsi::ClassExt base_class_QMediaBindableInterface_in_QRadioData (qtdecl_QMediaBindableInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QRadioData () { return decl_QRadioData; } } diff --git a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc index 8a175122b..96894a42e 100644 --- a/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc +++ b/src/gsiqt/qt5/QtMultimedia/gsiDeclQVideoWidget.cc @@ -559,6 +559,12 @@ qt_gsi::QtNativeClass decl_QVideoWidget (qtdecl_QWidget (), "QtMul methods_QVideoWidget (), "@hide\n@alias QVideoWidget"); +// Additional base classes + +gsi::Class &qtdecl_QMediaBindableInterface (); + +gsi::ClassExt base_class_QMediaBindableInterface_in_QVideoWidget (qtdecl_QMediaBindableInterface ()); + GSI_QTMULTIMEDIA_PUBLIC gsi::Class &qtdecl_QVideoWidget () { return decl_QVideoWidget; } } diff --git a/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc index 6e62851af..2b421a5af 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQAccessibleWidget.cc @@ -450,6 +450,12 @@ gsi::Class decl_QAccessibleWidget (qtdecl_QAccessibleObject ( "@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/qt5/QtWidgets/gsiDeclQGraphicsObject.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc index fcca75fb2..5b1a48938 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsObject.cc @@ -270,6 +270,12 @@ qt_gsi::QtNativeClass decl_QGraphicsObject (qtdecl_QObject (), 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/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc index 5dc180d41..f2e660315 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQGraphicsWidget.cc @@ -1359,6 +1359,12 @@ qt_gsi::QtNativeClass decl_QGraphicsWidget (qtdecl_QGraphicsObj 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/qt5/QtWidgets/gsiDeclQLayout.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc index f26fb3005..8d435a7bb 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQLayout.cc @@ -1020,6 +1020,12 @@ qt_gsi::QtNativeClass decl_QLayout (qtdecl_QObject (), "QtWidgets", "QL 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/qt5/QtWidgets/gsiDeclQWidget.cc b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc index afc1d36d3..918e7cc60 100644 --- a/src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc +++ b/src/gsiqt/qt5/QtWidgets/gsiDeclQWidget.cc @@ -4827,6 +4827,12 @@ qt_gsi::QtNativeClass decl_QWidget (qtdecl_QObject (), "QtWidgets", "QW 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/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc index 04548a09e..a7a7490de 100644 --- a/src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc +++ b/src/gsiqt/qt5/QtXml/gsiDeclQXmlDefaultHandler.cc @@ -777,6 +777,20 @@ gsi::Class decl_QXmlDefaultHandler (qtdecl_QXmlContentHandle 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_QTXML_PUBLIC gsi::Class &qtdecl_QXmlDefaultHandler () { return decl_QXmlDefaultHandler; } } From a0546098b3e13177838adb59a5642bfc9e390018 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 10:45:38 +0100 Subject: [PATCH 10/17] Fixed Qt5 binding generator config --- scripts/mkqtdecl5/mkqtdecl.conf | 1 + ...gsiExpression.cc => gsiExpressionTests.cc} | 0 src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc | 56 ------------------- 3 files changed, 1 insertion(+), 56 deletions(-) rename src/gsi/unit_tests/{gsiExpression.cc => gsiExpressionTests.cc} (100%) delete mode 100644 src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc diff --git a/scripts/mkqtdecl5/mkqtdecl.conf b/scripts/mkqtdecl5/mkqtdecl.conf index fa388a588..a9667acbb 100644 --- a/scripts/mkqtdecl5/mkqtdecl.conf +++ b/scripts/mkqtdecl5/mkqtdecl.conf @@ -204,6 +204,7 @@ drop_class "QtGlobal" drop_class "QThreadStorage" drop_class "QThreadStorageData" drop_class "QtMsgHandler" +drop_class "QtMsgType" drop_class "QtPlugin" drop_class "QtPluginInstanceFunction" drop_class "QtPrivate" diff --git a/src/gsi/unit_tests/gsiExpression.cc b/src/gsi/unit_tests/gsiExpressionTests.cc similarity index 100% rename from src/gsi/unit_tests/gsiExpression.cc rename to src/gsi/unit_tests/gsiExpressionTests.cc diff --git a/src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc b/src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc deleted file mode 100644 index 0f13177df..000000000 --- a/src/gsiqt/qt5/QtCore/gsiDeclQtMsgType.cc +++ /dev/null @@ -1,56 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2021 Matthias Koefferlein - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -/** -* @file gsiDeclQtMsgType.cc -* -* DO NOT EDIT THIS FILE. -* This file has been created automatically -*/ - -#include -#include "gsiQt.h" -#include "gsiQtCoreCommon.h" -#include - -// ----------------------------------------------------------------------- -// enum QtMsgType - - -// Implementation of the enum wrapper class for ::QtMsgType -namespace qt_gsi -{ - -static gsi::Enum decl_QtMsgType_Enum ("QtCore", "QtMsgType", - gsi::enum_const ("QtDebugMsg", QtDebugMsg, "@brief Enum constant QtDebugMsg") + - gsi::enum_const ("QtWarningMsg", QtWarningMsg, "@brief Enum constant QtWarningMsg") + - gsi::enum_const ("QtCriticalMsg", QtCriticalMsg, "@brief Enum constant QtCriticalMsg") + - gsi::enum_const ("QtFatalMsg", QtFatalMsg, "@brief Enum constant QtFatalMsg") + - gsi::enum_const ("QtInfoMsg", QtInfoMsg, "@brief Enum constant QtInfoMsg") + - gsi::enum_const ("QtSystemMsg", QtSystemMsg, "@brief Enum constant QtSystemMsg"), - "@qt\n@brief This class represents the QtMsgType enum"); - -static gsi::QFlagsClass decl_QtMsgType_Enums ("QtCore", "QFlags_QtMsgType", - "@qt\n@brief This class represents the QFlags flag set"); - -} - From ea83b4285d511af056491cff7b07e13488813b4a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 10:45:54 +0100 Subject: [PATCH 11/17] Enabling multiple bases and child classes for GSI expressions --- src/gsi/gsi/gsiExpression.cc | 108 ++++++++++++++++++----- src/gsi/unit_tests/gsiExpressionTests.cc | 24 +++++ src/gsi/unit_tests/unit_tests.pro | 2 +- 3 files changed, 113 insertions(+), 21 deletions(-) diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 1836f5899..2a638d851 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -106,6 +106,10 @@ public: std::pair find (bool st, const std::string &name) const { std::map, size_t>::const_iterator t = m_name_map.find (std::make_pair (st, name)); + if (! st && t == m_name_map.end ()) { + // can also use static methods for instances + t = m_name_map.find (std::make_pair (true, name)); + } if (t != m_name_map.end ()) { return std::make_pair (true, t->second); } else { @@ -1088,11 +1092,7 @@ initialize_expressions () // skip external classes continue; } else if ((*c)->declaration () != *c) { - // 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 should create a reference inside the - // target class. tl_assert ((*c)->parent () != 0); // top-level classes should be merged - // TODO: implement (see rba.cc:1544 for example) continue; } @@ -1360,6 +1360,65 @@ special_method_impl (gsi::MethodBase::special_method_type smt, tl::Variant &self return tl::Variant (); } +static std::pair find_method (const gsi::ClassBase *cls, bool as_static, const std::string &method) +{ + const ExpressionMethodTable *mt = 0; + size_t mid = 0; + + while (cls) { + + mt = ExpressionMethodTable::method_table_by_class (cls); + std::pair t = mt->find (as_static, method); + if (t.first) { + mid = t.second; + return std::make_pair (mt, mid); + } + + // try unnamed child classes as static + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + if (cc->name ().empty ()) { + std::pair m = find_method (cc->declaration (), true, method); + if (m.first) { + return m; + } + } + } + + cls = cls->base (); + + } + + return std::make_pair ((const ExpressionMethodTable *) 0, size_t (0)); +} + +static const gsi::ClassBase *find_class_scope (const gsi::ClassBase *cls, const std::string &name) +{ + while (cls) { + + // try named child classes + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + if (cc->name () == name) { + return cc->declaration (); + } + } + + // try unnamed child classes as additional bases + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + if (cc->name ().empty ()) { + const gsi::ClassBase *scope = find_class_scope (cc->declaration (), name); + if (scope) { + return scope; + } + } + } + + cls = cls->base (); + + } + + return 0; +} + void VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context*/, tl::Variant &out, tl::Variant &object, const std::string &method, const std::vector &args) const { @@ -1374,25 +1433,34 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context } } - const ExpressionMethodTable *mt = 0; - size_t mid = 0; - - const gsi::ClassBase *cls = clsact; - while (cls) { + auto m = find_method (clsact, mp_object_cls != 0 /*static*/, method); - mt = ExpressionMethodTable::method_table_by_class (cls); - std::pair t = mt->find (mp_object_cls != 0 /*static*/, method); - if (t.first) { - mid = t.second; - break; + const ExpressionMethodTable *mt = m.first; + size_t mid = m.second; + + if (! mt) { + + // try class scope + const gsi::ClassBase *scope = find_class_scope (clsact, method); + if (scope) { + + if (! args.empty ()) { + throw tl::Exception (tl::to_string (tr ("'%s' is not a function and cannot have parameters")), method); + } + + // we found a class scope: return a reference to that + const tl::VariantUserClassBase *scope_var_cls = scope->var_cls_cls (); + if (scope_var_cls) { + out = tl::Variant ((void *) 0, scope_var_cls, false); + } else { + out = tl::Variant (); + } + return; + + } else { + throw tl::Exception (tl::to_string (tr ("Unknown method '%s' of class '%s'")), method, clsact->name ()); } - cls = cls->base (); - - } - - if (cls == 0) { - throw tl::Exception (tl::to_string (tr ("Unknown method")) + " '" + method + "' of class '" + clsact->name () + "'"); } const gsi::MethodBase *meth = 0; diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index 11ea5fe31..ada8932a3 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -565,3 +565,27 @@ TEST(9) EXPECT_EQ (collect_func->values[1], 14400); EXPECT_EQ (collect_func->values[2], 19600); } + +TEST(10) +{ + tl::Eval e; + tl::Variant v; + + v = e.parse ("var b3 = B3.new(); b3.E.E3B").execute (); + EXPECT_EQ (v.to_string (), std::string ("E3B")); + v = e.parse ("B3.E.E3B").execute (); + EXPECT_EQ (v.to_string (), std::string ("E3B")); + v = e.parse ("var bb = BB.new(); bb.C1").execute (); + EXPECT_EQ (v.to_string (), std::string ("42")); + v = e.parse ("var bb = BB.new(); bb.C2").execute (); + EXPECT_EQ (v.to_string (), std::string ("17")); + v = e.parse ("var bb = BB.new(); bb.C3").execute (); + EXPECT_EQ (v.to_string (), std::string ("-1")); + v = e.parse ("var bb = BB.new(); bb.E.E3A").execute (); + EXPECT_EQ (v.to_string (), std::string ("E3A")); + v = e.parse ("BB.E.E3C").execute (); + EXPECT_EQ (v.to_string (), std::string ("E3C")); + v = e.parse ("var bb = BB.new(); bb.d3(BB.E.E3A, BB.E.E3C)").execute (); + EXPECT_EQ (v.to_string (), std::string ("2")); +} + diff --git a/src/gsi/unit_tests/unit_tests.pro b/src/gsi/unit_tests/unit_tests.pro index f7948182b..fb67a596b 100644 --- a/src/gsi/unit_tests/unit_tests.pro +++ b/src/gsi/unit_tests/unit_tests.pro @@ -7,7 +7,7 @@ TARGET = gsi_tests include($$PWD/../../lib_ut.pri) SOURCES = \ - gsiExpression.cc \ + gsiExpressionTests.cc HEADERS += \ From 8b87fba8d43b75bdcc02eb8e8a0da2d5d34acc68 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 10:46:24 +0100 Subject: [PATCH 12/17] Updated Qt5 bindings --- src/gsiqt/qt5/QtCore/QtCore.pri | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gsiqt/qt5/QtCore/QtCore.pri b/src/gsiqt/qt5/QtCore/QtCore.pri index 080d8c605..04b0ffc6b 100644 --- a/src/gsiqt/qt5/QtCore/QtCore.pri +++ b/src/gsiqt/qt5/QtCore/QtCore.pri @@ -179,8 +179,7 @@ SOURCES += \ $$PWD/gsiDeclQt_1.cc \ $$PWD/gsiDeclQt_2.cc \ $$PWD/gsiDeclQt_3.cc \ - $$PWD/gsiDeclQt_4.cc \ - $$PWD/gsiDeclQtMsgType.cc + $$PWD/gsiDeclQt_4.cc HEADERS += gsiQtCoreCommon.h From 4966601bd1ca609aa42575a979ec4ce119e1b557 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 14:05:09 +0100 Subject: [PATCH 13/17] Updated Qt4 bindings --- scripts/mkqtdecl4/mkqtdecl.conf | 20 +++++++++++++++++++ src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc | 6 ++++++ src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc | 6 ++++++ src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc | 6 ++++++ src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc | 6 ++++++ src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc | 5 +---- src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc | 6 +----- src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc | 6 ++++++ .../qt4/QtXml/gsiDeclQXmlDefaultHandler.cc | 14 +++++++++++++ 9 files changed, 66 insertions(+), 9 deletions(-) diff --git a/scripts/mkqtdecl4/mkqtdecl.conf b/scripts/mkqtdecl4/mkqtdecl.conf index de9123023..2b7c1713a 100644 --- a/scripts/mkqtdecl4/mkqtdecl.conf +++ b/scripts/mkqtdecl4/mkqtdecl.conf @@ -165,10 +165,30 @@ drop_class "QtGlobal" drop_class "QThreadStorage" drop_class "QThreadStorageData" drop_class "QtMsgHandler" +drop_class "QtMsgType" drop_class "QtPlugin" drop_class "QtPluginInstanceFunction" drop_class "QtPrivate" drop_class "QtSharedPointer" +drop_class "QtValidLicenseForActiveQtModule" +drop_class "QtValidLicenseForCoreModule" +drop_class "QtValidLicenseForDBusModule" +drop_class "QtValidLicenseForDeclarativeModule" +drop_class "QtValidLicenseForGuiModule" +drop_class "QtValidLicenseForHelpModule" +drop_class "QtValidLicenseForMultimediaModule" +drop_class "QtValidLicenseForNetworkModule" +drop_class "QtValidLicenseForOpenGLModule" +drop_class "QtValidLicenseForOpenVGModule" +drop_class "QtValidLicenseForQt3SupportLightModule" +drop_class "QtValidLicenseForQt3SupportModule" +drop_class "QtValidLicenseForScriptModule" +drop_class "QtValidLicenseForScriptToolsModule" +drop_class "QtValidLicenseForSqlModule" +drop_class "QtValidLicenseForSvgModule" +drop_class "QtValidLicenseForTestModule" +drop_class "QtValidLicenseForXmlModule" +drop_class "QtValidLicenseForXmlPatternsModule" drop_class "QTypeInfo" drop_class "QUuid" drop_class "QUpdateLaterEvent" diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc b/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc index 59aec216f..5c6651407 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQDropEvent.cc @@ -327,6 +327,12 @@ gsi::Class decl_QDropEvent (qtdecl_QEvent (), "QtGui", "QDropEvent_N methods_QDropEvent (), "@hide\n@alias QDropEvent"); +// Additional base classes + +gsi::Class &qtdecl_QMimeSource (); + +gsi::ClassExt base_class_QMimeSource_in_QDropEvent (qtdecl_QMimeSource ()); + GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QDropEvent () { return decl_QDropEvent; } } diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc index cd8b24b52..62ac8be96 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsObject.cc @@ -311,6 +311,12 @@ qt_gsi::QtNativeClass decl_QGraphicsObject (qtdecl_QObject (), methods_QGraphicsObject (), "@hide\n@alias QGraphicsObject"); +// Additional base classes + +gsi::Class &qtdecl_QGraphicsItem (); + +gsi::ClassExt base_class_QGraphicsItem_in_QGraphicsObject (qtdecl_QGraphicsItem ()); + GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QGraphicsObject () { return decl_QGraphicsObject; } } diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc index 4019d1e40..edb1a1ec1 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQGraphicsWidget.cc @@ -1361,6 +1361,12 @@ qt_gsi::QtNativeClass decl_QGraphicsWidget (qtdecl_QGraphicsObj methods_QGraphicsWidget (), "@hide\n@alias QGraphicsWidget"); +// Additional base classes + +gsi::Class &qtdecl_QGraphicsLayoutItem (); + +gsi::ClassExt base_class_QGraphicsLayoutItem_in_QGraphicsWidget (qtdecl_QGraphicsLayoutItem ()); + GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QGraphicsWidget () { return decl_QGraphicsWidget; } } diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc b/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc index 792f3c696..919614171 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQLayout.cc @@ -1022,6 +1022,12 @@ qt_gsi::QtNativeClass decl_QLayout (qtdecl_QObject (), "QtGui", "QLayou methods_QLayout (), "@hide\n@alias QLayout"); +// Additional base classes + +gsi::Class &qtdecl_QLayoutItem (); + +gsi::ClassExt base_class_QLayoutItem_in_QLayout (qtdecl_QLayoutItem ()); + GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QLayout () { return decl_QLayout; } } diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc index 2bf079574..f110e9317 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPolygon.cc @@ -27,11 +27,8 @@ * This file has been created automatically */ -#include -#include -#include -#include #include +#include #include "gsiQt.h" #include "gsiQtGuiCommon.h" #include diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc b/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc index 1451ba452..2fcb2d74e 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQPolygonF.cc @@ -27,12 +27,8 @@ * This file has been created automatically */ -#include -#include -#include -#include -#include #include +#include #include "gsiQt.h" #include "gsiQtGuiCommon.h" #include diff --git a/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc b/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc index 592300861..8395e6b82 100644 --- a/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc +++ b/src/gsiqt/qt4/QtGui/gsiDeclQWidget.cc @@ -4793,6 +4793,12 @@ qt_gsi::QtNativeClass decl_QWidget (qtdecl_QObject (), "QtGui", "QWidge methods_QWidget (), "@hide\n@alias QWidget"); +// Additional base classes + +gsi::Class &qtdecl_QPaintDevice (); + +gsi::ClassExt base_class_QPaintDevice_in_QWidget (qtdecl_QPaintDevice ()); + GSI_QTGUI_PUBLIC gsi::Class &qtdecl_QWidget () { return decl_QWidget; } } diff --git a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc index 04548a09e..a7a7490de 100644 --- a/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc +++ b/src/gsiqt/qt4/QtXml/gsiDeclQXmlDefaultHandler.cc @@ -777,6 +777,20 @@ gsi::Class decl_QXmlDefaultHandler (qtdecl_QXmlContentHandle 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_QTXML_PUBLIC gsi::Class &qtdecl_QXmlDefaultHandler () { return decl_QXmlDefaultHandler; } } From eecda3222bd50e36163dd622619f1585b0663a92 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 15:17:16 +0100 Subject: [PATCH 14/17] Fixed pymod_tests --- src/pymod/unit_tests/pymod_tests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pymod/unit_tests/pymod_tests.cc b/src/pymod/unit_tests/pymod_tests.cc index a04e3c9ad..ec2fe4de2 100644 --- a/src/pymod/unit_tests/pymod_tests.cc +++ b/src/pymod/unit_tests/pymod_tests.cc @@ -138,7 +138,7 @@ PYMODTEST (import_QtSvg, "import_QtSvg.py") PYMODTEST (import_QtXmlPatterns, "import_QtXmlPatterns.py") #endif -#if QT_VERSION >= 0x50000 +#if QT_VERSION >= 0x60000 PYMODTEST (import_QtCore5Compat, "import_QtCore5Compat.py") #endif From 9aed8529f8eece316faefe5e9d3c547c16f8d35e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 16:37:05 +0100 Subject: [PATCH 15/17] Build needs C++11 --- src/klayout.pri | 2 +- src/pymod/bridge_sample/bridge_sample.pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/klayout.pri b/src/klayout.pri index afd37597b..5dcc9b30f 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -152,7 +152,7 @@ msvc { -Wno-reserved-user-defined-literal \ # because we use unordered_map/unordered_set: - QMAKE_CXXFLAGS += -std=c++0x + QMAKE_CXXFLAGS += -std=c++11 win32 { QMAKE_LFLAGS += -Wl,--exclude-all-symbols diff --git a/src/pymod/bridge_sample/bridge_sample.pro b/src/pymod/bridge_sample/bridge_sample.pro index 73ed68054..372729f5e 100644 --- a/src/pymod/bridge_sample/bridge_sample.pro +++ b/src/pymod/bridge_sample/bridge_sample.pro @@ -92,7 +92,7 @@ LIBS += -L$$LIBDIR -lklayout_db -Wno-reserved-user-defined-literal \ # because we use unordered_map/unordered_set: - QMAKE_CXXFLAGS += -std=c++0x + QMAKE_CXXFLAGS += -std=c++11 # Python is somewhat sloppy and relies on the compiler initializing fields # of strucs to 0: From 0c142c9252bbd825bf71562acffe597c688a781e Mon Sep 17 00:00:00 2001 From: klayoutmatthias Date: Sat, 11 Dec 2021 18:56:44 +0000 Subject: [PATCH 16/17] gcc 4.x (CentOS 7) does not support all C++11 type traits --- src/tl/tl/tlReuseVector.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tl/tl/tlReuseVector.h b/src/tl/tl/tlReuseVector.h index 8805fc135..34ef9cdab 100644 --- a/src/tl/tl/tlReuseVector.h +++ b/src/tl/tl/tlReuseVector.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -491,7 +492,12 @@ private: * One requirement is that sizeof(C) >= sizeof(void *). */ +#if __GNUC__ >= 5 template ::value> +#else +// no support for extended type traits in gcc 4.x +template +#endif class reuse_vector { public: From b6a1d035de26589781dbd95d8f93ba3d2688090d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Dec 2021 20:59:10 +0100 Subject: [PATCH 17/17] Added version to changelog so Debian packages can be made --- Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog b/Changelog index 9c130dd01..49835cc05 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,7 @@ +0.27.6 (2021-xx-xx): +TODO + 0.27.5 (2021-11-14): * Enhancements: Better support for execution (also cross-interpreter) of macro code from scripts - New methods for the Macro class like "run" and loading from file