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 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/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/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..47fa3057f 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 @@ -1096,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 @@ -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,24 @@ END 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| + 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 +3246,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/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/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/gsi/unit_tests/gsiExpression.cc b/src/gsi/unit_tests/gsiExpressionTests.cc similarity index 96% rename from src/gsi/unit_tests/gsiExpression.cc rename to src/gsi/unit_tests/gsiExpressionTests.cc index 11ea5fe31..ada8932a3 100644 --- a/src/gsi/unit_tests/gsiExpression.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 += \ 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; } } 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; } } diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc b/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc index 3b83e14d9..211b96080 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQIODevice.cc @@ -847,6 +847,12 @@ qt_gsi::QtNativeClass decl_QIODevice (qtdecl_QObject (), "QtCore", "Q "@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/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/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc b/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc index fc7eba35c..021890ad0 100644 --- a/src/gsiqt/qt6/QtCore5Compat/gsiDeclQXmlDefaultHandler.cc +++ b/src/gsiqt/qt6/QtCore5Compat/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_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..8a2743c79 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQOffscreenSurface.cc @@ -314,6 +314,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/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc b/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc index 5e3d6df43..119ad5f89 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQPaintDeviceWindow.cc @@ -298,6 +298,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/qt6/QtGui/gsiDeclQPdfWriter.cc b/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc index 6b134be11..b8d88eef4 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQPdfWriter.cc @@ -383,6 +383,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/qt6/QtGui/gsiDeclQWindow.cc b/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc index dd8faab17..ad25080ed 100644 --- a/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc +++ b/src/gsiqt/qt6/QtGui/gsiDeclQWindow.cc @@ -2207,6 +2207,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/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc index 134294b90..73987340e 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQAccessibleWidget.cc @@ -451,6 +451,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/qt6/QtWidgets/gsiDeclQGraphicsObject.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsObject.cc index adb8b93a6..099cec461 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 @@ -243,6 +244,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/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc index ecc208895..2cbdee756 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQGraphicsWidget.cc @@ -1375,6 +1375,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/qt6/QtWidgets/gsiDeclQLayout.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc index 0761305dd..c8c777f38 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQLayout.cc @@ -1014,6 +1014,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/qt6/QtWidgets/gsiDeclQWidget.cc b/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc index 5572da466..506bac75d 100644 --- a/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc +++ b/src/gsiqt/qt6/QtWidgets/gsiDeclQWidget.cc @@ -4982,6 +4982,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/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/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index acaab2780..19318550b 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); @@ -2308,6 +2354,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) { @@ -2319,39 +2371,542 @@ PythonModule::python_doc (const gsi::MethodBase *method) } } -void -PythonModule::check (const char *mod_name) +namespace { - if (! mod_name) { - return; + +class PythonClassGenerator +{ +public: + PythonClassGenerator (PythonModule *module, PyObject *all_list) + : mp_module (module), m_all_list (all_list) + { + // .. nothing yet .. } - // 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) { + // needs to be called before for each extension before the classes are made + 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 ()); + } + } - if (c->module () != mod_name) { - // don't handle classes outside this module - continue; + PyTypeObject *make_class (const gsi::ClassBase *cls, bool as_static) + { + // 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; } - if (PythonClassClientData::py_type (*c)) { - // don't handle classes twice - continue; + PythonRef bases; + + // 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 ()); } - // 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)); + bases = PythonRef (PyTuple_New (n_bases)); + + int ibase = 0; + if (cls->base () != 0) { + 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, true); + Py_INCREF (base); + PyTuple_SetItem (bases.get (), ibase++, base); } } - // 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)); + // creates the type object + + PythonRef dict (PyDict_New ()); + 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)); + 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 ()); + + PyTypeObject *type = (PyTypeObject *) PyObject_Call ((PyObject *) &PyType_Type, args.get (), NULL); + if (type == NULL) { + check_error (); + tl_assert (false); } + // Customize + 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, 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 + + 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) { + 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); + } + } + + // Build the attributes now ... + + MethodTable *mt = MethodTable::method_table_by_class (cls); + + // produce the properties + + if (! as_static) { + + for (size_t mid = mt->bottom_property_mid (); mid < mt->top_property_mid (); ++mid) { + + 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; + + 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 (); + } + + const std::string &name = mt->property_name (mid); + + // 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); + } + } + + } + + 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)); + } + + 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)); + } + + 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); + + } + + } + + // collect the names which have been disambiguated static/non-static wise + std::vector disambiguated_names; + + // check, whether there is an "inspect" method + bool has_inspect = false; + for (size_t mid = mt->bottom_mid (); mid < mt->top_mid () && ! has_inspect; ++mid) { + has_inspect = (mt->name (mid) == "inspect"); + } + + // produce the methods now + for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) { + + std::string name = mt->name (mid); + + // extract a suitable Python name + name = extract_python_name (name); + + // cannot extract a Python name + if (name.empty ()) { + + // drop non-standard names + if (tl::verbosity () >= 20) { + tl::warn << tl::to_string (tr ("Class ")) << cls->name () << ": " << tl::to_string (tr ("no Python mapping for method ")) << mt->name (mid); + } + + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is not available for Python"))); + + } else { + + std::string raw_name = name; + + // does this method hide a property? -> append "_" in that case + std::pair t = mt->find_property (mt->is_static (mid), name); + if (t.first) { + name += "_"; + } + + // needs static/non-static disambiguation? + t = mt->find_method (! mt->is_static (mid), name); + if (t.first) { + + disambiguated_names.push_back (name); + if (mt->is_static (mid)) { + name = "_class_" + name; + } else { + name = "_inst_" + name; + } + + } else if (is_reserved_word (name)) { + + // drop non-standard names + if (tl::verbosity () >= 20) { + tl::warn << tl::to_string (tr ("Class ")) << cls->name () << ": " << tl::to_string (tr ("no Python mapping for method (reserved word) ")) << name; + } + + name += "_"; + + } + + if (name != raw_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 + std::string doc; + for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) { + if (! doc.empty ()) { + doc = "\n\n"; + } + doc += (*m)->doc (); + } + + const gsi::MethodBase *m_first = *mt->begin (mid); + + tl_assert (mid < sizeof (method_adaptors) / sizeof (method_adaptors[0])); + if (! mt->is_static (mid)) { + + if (! as_static) { + + std::vector alt_names; + + 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; +#else + 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)'"))); + 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 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_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); + + } + + 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 ()) { + + // static methods without arguments which start with a capital letter are treated as constants + PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (mp_module->make_string (name)); + desc->type = type; + desc->getter = method_adaptors[mid]; + + PythonRef attr (desc); + 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): " << cls->name () << "." << name; + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This attribute is not available for Python"))); + } + + } else if (! as_static) { + + 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 + mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is the default initializer of the object"))); + + PyMethodDef *method = mp_module->make_method_def (); + method->ml_name = "__init__"; + method->ml_meth = (PyCFunction) method_init_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, method->ml_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 | METH_CLASS; + + PythonRef attr = PythonRef (PyDescr_NewClassMethod (type, method)); + set_type_attr (type, name, attr); + + } + + } + + 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" + + 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_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; + + 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); + + } + + } + + } + + // 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 or disambiguator is not required -> don't install it + Py_XDECREF (attr_inst); + Py_XDECREF (attr_class); + PyErr_Clear (); + + } 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); + + } + + } + + } + + mt->finish (); + + return type; } + +private: + PythonModule *mp_module; + PyObject *m_all_list; + std::map > m_extensions_for; +}; + } void @@ -2384,524 +2939,32 @@ PythonModule::make_classes (const char *mod_name) 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, 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 using all available classes 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) { - 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; + gen.register_extension (*c); } + } - // 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; - 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)); + // 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, false); } - - 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 ()); - - PythonRef args (PyTuple_New (3)); - PyTuple_SetItem (args.get (), 0, c2python ((*c)->name ())); - PyTuple_SetItem (args.get (), 1, bases.release ()); - PyTuple_SetItem (args.get (), 2, dict.release ()); - - PyTypeObject *type = (PyTypeObject *) PyObject_Call ((PyObject *) &PyType_Type, args.get (), NULL); - if (type == NULL) { - check_error (); - tl_assert (false); - } - - // 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; - - PythonClassClientData::initialize (**c, type); - - tl_assert (cls_for_type (type) == *c); - - // 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 ()); - PythonRef attr ((PyObject *) type); - set_type_attr (parent_type, (*c)->name ().c_str (), attr); - } else { - PyList_Append (all_list.get (), PythonRef (c2python ((*c)->name ())).get ()); - PyModule_AddObject (module, (*c)->name ().c_str (), (PyObject *) type); - } - - // Build the attributes now ... - - MethodTable *mt = MethodTable::method_table_by_class (*c); - - // signals are translated into the setters and getters - for (gsi::ClassBase::method_iterator m = (*c)->begin_methods (); m != (*c)->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 = (*c)->begin_methods (); m != (*c)->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 = (*c)->begin_methods (); m != (*c)->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) { - - 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; - - 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 (); - } - - 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 MethodTable *mt_base = MethodTable::method_table_by_class (cls); - 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); - } - } - - } - - 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 (); - m_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"; - } - 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); - } - - PythonRef attr; - - if (! is_static) { - - // non-static attribute getters/setters - PyGetSetDef *getset = make_getset_def (); - getset->name = 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->closure = make_closure (getter_mid, setter_mid); - - attr = PythonRef (PyDescr_NewGetSet (type, getset)); - - } else { - - PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (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); - - } - - // collect the names which have been disambiguated static/non-static wise - std::vector disambiguated_names; - - // check, whether there is an "inspect" method - bool has_inspect = false; - for (size_t mid = mt->bottom_mid (); mid < mt->top_mid () && ! has_inspect; ++mid) { - has_inspect = (mt->name (mid) == "inspect"); - } - - // produce the methods now - for (size_t mid = mt->bottom_mid (); mid < mt->top_mid (); ++mid) { - - std::string name = mt->name (mid); - - // extract a suitable Python name - name = extract_python_name (name); - - // cannot extract a Python name - if (name.empty ()) { - - // 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); - } - - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is not available for Python"))); - - } else { - - std::string raw_name = name; - - // does this method hide a property? -> append "_" in that case - std::pair t = mt->find_property (mt->is_static (mid), name); - if (t.first) { - name += "_"; - } - - // needs static/non-static disambiguation? - t = mt->find_method (! mt->is_static (mid), name); - if (t.first) { - - disambiguated_names.push_back (name); - if (mt->is_static (mid)) { - name = "_class_" + name; - } else { - name = "_inst_" + name; - } - - } else if (is_reserved_word (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; - } - - 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)); - } - - // create documentation - std::string doc; - for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) { - if (! doc.empty ()) { - doc = "\n\n"; - } - doc += (*m)->doc (); - } - - const gsi::MethodBase *m_first = *mt->begin (mid); - - tl_assert (mid < sizeof (method_adaptors) / sizeof (method_adaptors[0])); - if (! mt->is_static (mid)) { - - std::vector alt_names; - - 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; -#else - 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)'"))); - 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)'"))); - } - - } 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)'"))); - - } 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)'"))); - 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)'"))); - 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"))); - 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__'"))); - 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 = make_method_def (); - method->ml_name = make_string (*an); - method->ml_meth = (PyCFunction) method_adaptors[mid]; - method->ml_doc = make_string (doc); - method->ml_flags = METH_VARARGS; - - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, *an, attr); - - } - - PyMethodDef *method = make_method_def (); - method->ml_name = make_string (name); - method->ml_meth = (PyCFunction) method_adaptors[mid]; - method->ml_doc = 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 ()) { - - // static methods without arguments which start with a capital letter are treated as constants - PYAStaticAttributeDescriptorObject *desc = PYAStaticAttributeDescriptorObject::create (make_string (name)); - desc->type = type; - desc->getter = method_adaptors[mid]; - - PythonRef attr (desc); - 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"))); - } - - } else { - - 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"))); - - PyMethodDef *method = make_method_def (); - method->ml_name = "__init__"; - method->ml_meth = (PyCFunction) method_init_adaptors[mid]; - method->ml_doc = make_string (doc); - method->ml_flags = METH_VARARGS; - - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); - - } - - PyMethodDef *method = make_method_def (); - method->ml_name = make_string (name); - method->ml_meth = (PyCFunction) method_adaptors[mid]; - method->ml_doc = make_string (doc); - method->ml_flags = METH_VARARGS | METH_CLASS; - - PythonRef attr = PythonRef (PyDescr_NewClassMethod (type, method)); - set_type_attr (type, name, attr); - - } - - } - - } - - // 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; - - if (! has_cmp && has_eq) { - - if (! has_ne) { - - // Add a definition for "__ne__" - PyMethodDef *method = 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); - - } - - if (has_lt && ! has_le) { - - // Add a definition for "__le__" - PyMethodDef *method = 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 = 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 = 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 " << (*c)->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); - - } - - } - - mt->finish (); - } } @@ -2925,7 +2988,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 beb510926..af21dbe48 100644 --- a/src/pya/pya/pyaModule.h +++ b/src/pya/pya/pyaModule.h @@ -108,13 +108,56 @@ public: */ PyObject *take_module (); -private: - void add_python_doc (const gsi::ClassBase &cls, const MethodTable *mt, int mid, const std::string &doc); - PyMethodDef *make_method_def (); - PyGetSetDef *make_getset_def (); - char *make_string (const std::string &s); - static void check (const char *mod_name); + /** + * @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: 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/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: diff --git a/src/pymod/unit_tests/pymod_tests.cc b/src/pymod/unit_tests/pymod_tests.cc index 83fdd0c5c..ec2fe4de2 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 >= 0x60000 +PYMODTEST (import_QtCore5Compat, "import_QtCore5Compat.py") +#endif + #endif PYMODTEST (import_pya, "pya_tests.py") diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 7aaa7899e..ab7d56a09 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,100 @@ 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 ()) { + // got an extension + tl_assert (cls->parent ()); + m_extensions_for [cls->parent ()->declaration ()].push_back (cls->declaration ()); + } + } - std::list sorted_classes = gsi::ClassBase::classes_in_definition_order (); - for (std::list::const_iterator c = sorted_classes.begin (); c != sorted_classes.end (); ++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 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; + VALUE make_class (const gsi::ClassBase *cls, bool as_static, VALUE parent_class = (VALUE) 0, const gsi::ClassBase *parent = 0) + { + if (is_registered (cls, as_static)) { + return ruby_cls (cls, as_static); } 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 (), as_static); } 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 (as_static) { + + if (tl::verbosity () >= 20) { + tl::log << tl::to_string (tr ("Registering class as Ruby module:) ")) << 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 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 { - klass = rb_define_class_under (module, (*c)->name ().c_str (), 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 (), super); + } + + rb_define_alloc_func (klass, alloc_proxy); + } - register_class (klass, *c); + register_class (klass, cls, as_static); - rb_define_alloc_func (klass, alloc_proxy); + // mix-in unnamed extensions - MethodTable *mt = MethodTable::method_table_by_class (*c, true /*force init*/); + 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, true); + rb_include_module (klass, ext_module); + rb_extend_object (klass, ext_module); + } + } - for (gsi::ClassBase::method_iterator m = (*c)->begin_methods (); m != (*c)->end_methods (); ++m) { + // produce the child classes + + for (auto cc = cls->begin_child_classes (); cc != cls->end_child_classes (); ++cc) { + if (! cc->name ().empty ()) { + 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); + } + } + } + + 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 +1630,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 +1645,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 +1688,149 @@ 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 (! as_static) { - 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; +}; + +} + +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, false); + } } // 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/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/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: 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) + + 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() diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 1d86811b6..de8e911b3 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -3012,6 +3012,24 @@ 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(RBA::B3::E::E3B.to_i, 101) # B3 + + 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