From fbd4c84d8440c3a34051b6be4fb6473423e5c899 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Oct 2022 23:26:14 +0200 Subject: [PATCH] Refactoring of pya/rba/expressions Goal is to support "*!" which is a variant of multiplication without the commutative nature. The refactoring yields a more consistent handling of Python specializations. Now this happens when the methods are collected, rather than later. This way, specific behavior for identically named synonyms can be implemented for example. This is the case for the "*" operator in Trans which is partially commutative and partially not. --- setup.py | 2 +- src/db/db/gsiDeclDbTrans.cc | 16 +- src/gsi/gsi/gsiExpression.cc | 3 + src/gsi/gsi/gsiMethods.cc | 2 +- src/gsi/unit_tests/gsiExpressionTests.cc | 9 + src/lay/lay/layGSIHelpProvider.cc | 2 + src/pya/pya/pyaModule.cc | 1399 +++++++++++----------- src/rba/rba/rba.cc | 15 +- 8 files changed, 766 insertions(+), 682 deletions(-) diff --git a/setup.py b/setup.py index d2e3e70c5..8e20b62c4 100644 --- a/setup.py +++ b/setup.py @@ -932,5 +932,5 @@ if __name__ == "__main__": include_package_data=True, ext_modules=[_tl, _gsi, _pya, _rba, _db, _lib, _rdb, _lym, _laybasic, _layview, _ant, _edt, _img] + db_plugins - + [tl, db, lib, rdb, lay]) + + [tl, db, lib, rdb, lay] ) diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index a6bfe92c7..e5101eaf8 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -304,7 +304,7 @@ struct trans_defs "\n" "This convenience method has been introduced in version 0.25." ) + - method ("*", &C::concat, arg ("t"), + method ("*!", &C::concat, arg ("t"), "@brief Returns the concatenated transformation\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" @@ -829,7 +829,7 @@ struct cplx_trans_defs "\n" "This convenience method has been introduced in version 0.25." ) + - method ("*", (C (C::*) (const C &c) const) &C::concat_same, arg ("t"), + method ("*!", (C (C::*) (const C &c) const) &C::concat_same, arg ("t"), "@brief Returns the concatenated transformation\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" @@ -1047,7 +1047,7 @@ Class decl_DCplxTrans ("db", "DCplxTrans", "\n" "This method has been introduced in version 0.25." ) + - method ("*", (db::CplxTrans (db::DCplxTrans::*) (const db::CplxTrans &) const) &db::DCplxTrans::concat, gsi::arg ("t"), + method ("*!", (db::CplxTrans (db::DCplxTrans::*) (const db::CplxTrans &) const) &db::DCplxTrans::concat, gsi::arg ("t"), "@brief Multiplication (concatenation) of transformations\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" @@ -1126,7 +1126,7 @@ Class decl_CplxTrans ("db", "CplxTrans", "\n" "This method has been introduced in version 0.25." ) + - method ("*", (db::DCplxTrans (db::CplxTrans::*) (const db::VCplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"), + method ("*!", (db::DCplxTrans (db::CplxTrans::*) (const db::VCplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"), "@brief Multiplication (concatenation) of transformations\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" @@ -1134,7 +1134,7 @@ Class decl_CplxTrans ("db", "CplxTrans", "@param t The transformation to apply before\n" "@return The modified transformation\n" ) + - method ("*", (db::CplxTrans (db::CplxTrans::*) (const db::ICplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"), + method ("*!", (db::CplxTrans (db::CplxTrans::*) (const db::ICplxTrans &) const) &db::CplxTrans::concat, gsi::arg ("t"), "@brief Multiplication (concatenation) of transformations\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" @@ -1216,7 +1216,7 @@ Class decl_ICplxTrans ("db", "ICplxTrans", "\n" "This method has been introduced in version 0.25." ) + - method ("*", (db::VCplxTrans (db::ICplxTrans::*) (const db::VCplxTrans &) const) &db::ICplxTrans::concat, gsi::arg ("t"), + method ("*!", (db::VCplxTrans (db::ICplxTrans::*) (const db::VCplxTrans &) const) &db::ICplxTrans::concat, gsi::arg ("t"), "@brief Multiplication (concatenation) of transformations\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" @@ -1288,7 +1288,7 @@ Class decl_VCplxTrans ("db", "VCplxTrans", "\n" "This method has been introduced in version 0.25." ) + - method ("*", (db::VCplxTrans (db::VCplxTrans::*) (const db::DCplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"), + method ("*!", (db::VCplxTrans (db::VCplxTrans::*) (const db::DCplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"), "@brief Multiplication (concatenation) of transformations\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" @@ -1296,7 +1296,7 @@ Class decl_VCplxTrans ("db", "VCplxTrans", "@param t The transformation to apply before\n" "@return The modified transformation\n" ) + - method ("*", (db::ICplxTrans (db::VCplxTrans::*) (const db::CplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"), + method ("*!", (db::ICplxTrans (db::VCplxTrans::*) (const db::CplxTrans &) const) &db::VCplxTrans::concat, gsi::arg ("t"), "@brief Multiplication (concatenation) of transformations\n" "\n" "The * operator returns self*t (\"t is applied before this transformation\").\n" diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index abda57030..1254e519e 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -199,6 +199,9 @@ private: for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) { if (syn->is_setter) { add_method (syn->name + "=", *m); + } else if (syn->name == "*!") { + // non-commutative multiplication + add_method ("*", *m); } else { add_method (syn->name, *m); } diff --git a/src/gsi/gsi/gsiMethods.cc b/src/gsi/gsi/gsiMethods.cc index f49813aeb..4350565dc 100644 --- a/src/gsi/gsi/gsiMethods.cc +++ b/src/gsi/gsi/gsiMethods.cc @@ -85,7 +85,7 @@ void MethodBase::parse_name (const std::string &name) { const char *n = name.c_str (); - if (*n == '*' && n[1] && n[1] != '*' && n[1] != '=') { + if (*n == '*' && n[1] && n[1] != '*' && n[1] != '!' && n[1] != '=') { m_protected = true; ++n; } diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index ac47b9a3e..1fdd8fb2e 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -589,3 +589,12 @@ TEST(10) EXPECT_EQ (v.to_string (), std::string ("2")); } +TEST(11) +{ + tl::Eval e; + tl::Variant v; + + // mapping of *! to *: + v = e.parse ("var b = Trans.new(1)*Trans.new(Vector.new(10, 20))").execute (); + EXPECT_EQ (v.to_string (), std::string ("r90 -20,10")); +} diff --git a/src/lay/lay/layGSIHelpProvider.cc b/src/lay/lay/layGSIHelpProvider.cc index 280220dc8..670833917 100644 --- a/src/lay/lay/layGSIHelpProvider.cc +++ b/src/lay/lay/layGSIHelpProvider.cc @@ -149,6 +149,8 @@ full_name (const gsi::MethodBase::MethodSynonym &syn) return syn.name + "?"; } else if (syn.is_setter) { return syn.name + "="; + } else if (syn.name == "*!") { + return "*"; } else { return syn.name; } diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index ac5de2ffb..7f54cc25f 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -37,416 +37,6 @@ namespace pya { -// ------------------------------------------------------------------- -// The lookup table for the method overload resolution - -/** - * @brief A single entry in the method table - * This class provides an entry for one name. It provides flags - * (ctor, static, protected) for the method and a list of implementations - * (gsi::MethodBase objects) - */ -class MethodTableEntry -{ -public: - typedef std::vector::const_iterator method_iterator; - - MethodTableEntry (const std::string &name, bool st, bool prot) - : m_name (name), m_is_static (st), m_is_protected (prot) - { } - - const std::string &name () const - { - return m_name; - } - - bool is_static () const - { - return m_is_static; - } - - bool is_protected () const - { - return m_is_protected; - } - - void add (const gsi::MethodBase *m) - { - m_methods.push_back (m); - } - - void finish () - { - // remove duplicate entries in the method list - std::vector m = m_methods; - std::sort(m.begin (), m.end ()); - m_methods.assign (m.begin (), std::unique (m.begin (), m.end ())); - } - - method_iterator begin () const - { - return m_methods.begin (); - } - - method_iterator end () const - { - return m_methods.end (); - } - -private: - std::string m_name; - bool m_is_static : 1; - bool m_is_protected : 1; - std::vector m_methods; -}; - -/** - * @brief The method table for a class - * The method table will provide the methods associated with a native method, i.e. - * a certain name. It only provides the methods, not a overload resolution strategy. - */ -class MethodTable -{ -public: - /** - * @brief Constructor - * This constructor will create a method table for the given class and register - * this table under this class. - */ - 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); - m_method_offset = base_mt->top_mid (); - m_property_offset = base_mt->top_property_mid (); - } - } - - /** - * @brief Returns the lowest method ID within the space of this table - * Method IDs below this one are reserved for base class methods - */ - size_t bottom_mid () const - { - return m_method_offset; - } - - /** - * @brief Returns the topmost + 1 method ID. - */ - size_t top_mid () const - { - return m_method_offset + m_table.size (); - } - - /** - * @brief Returns the lowest property method ID within the space of this table - * Method IDs below this one are reserved for base class methods - */ - size_t bottom_property_mid () const - { - return m_property_offset; - } - - /** - * @brief Returns the topmost + 1 property method ID. - */ - size_t top_property_mid () const - { - return m_property_offset + m_property_table.size (); - } - - /** - * @brief Find a method with the given name and static flag - * Returns true or false in the first part (true, if found) and - * the MID in the second part. - */ - std::pair find_method (bool st, const std::string &name) const - { - std::map , size_t>::const_iterator t = m_name_map.find (std::make_pair (st, name)); - if (t != m_name_map.end ()) { - return std::make_pair (true, t->second + m_method_offset); - } else { - return std::make_pair (false, 0); - } - } - - /** - * @brief Find a property with the given name and static flag - * Returns true or false in the first part (true, if found) and - * the MID in the second part. - */ - std::pair find_property (bool st, const std::string &name) const - { - std::map , size_t>::const_iterator t = m_property_name_map.find (std::make_pair (st, name)); - if (t != m_property_name_map.end ()) { - return std::make_pair (true, t->second + m_property_offset); - } else { - return std::make_pair (false, 0); - } - } - - /** - * @brief Adds a method to the table - */ - void add_method (const std::string &name, const gsi::MethodBase *mb) - { - bool st = mb->is_static (); - - std::map, size_t>::iterator n = m_name_map.find (std::make_pair (st, name)); - if (n == m_name_map.end ()) { - - m_name_map.insert (std::make_pair (std::make_pair(st, name), m_table.size ())); - m_table.push_back (MethodTableEntry (name, mb->is_static (), mb->is_protected ())); - m_table.back ().add (mb); - - } else { - - if (m_table [n->second].is_protected () != mb->is_protected ()) { - tl::warn << "Class " << mp_cls_decl->name () << ": method '" << name << " is both a protected and non-protected"; - } - - m_table [n->second].add (mb); - - } - } - - /** - * @brief Adds a setter with the given name - */ - void add_setter (const std::string &name, const gsi::MethodBase *setter) - { - bool st = setter->is_static (); - - std::map, size_t>::iterator n = m_property_name_map.find (std::make_pair (st, name)); - if (n == m_property_name_map.end ()) { - - m_property_name_map.insert (std::make_pair (std::make_pair(st, name), m_property_table.size ())); - m_property_table.push_back (std::make_pair (MethodTableEntry (name, st, false), MethodTableEntry (name, st, false))); - m_property_table.back ().first.add (setter); - - } else { - - m_property_table [n->second].first.add (setter); - - } - } - - /** - * @brief Adds a getter with the given name - */ - void add_getter (const std::string &name, const gsi::MethodBase *getter) - { - bool st = getter->is_static (); - - std::map, size_t>::iterator n = m_property_name_map.find (std::make_pair (st, name)); - if (n == m_property_name_map.end ()) { - - m_property_name_map.insert (std::make_pair (std::make_pair(st, name), m_property_table.size ())); - m_property_table.push_back (std::make_pair (MethodTableEntry (name, st, false), MethodTableEntry (name, st, false))); - m_property_table.back ().second.add (getter); - - } else { - - m_property_table [n->second].second.add (getter); - - } - } - - /** - * @brief Returns true if the method with the given ID is static - */ - bool is_static (size_t mid) const - { - return m_table [mid - m_method_offset].is_static (); - } - - /** - * @brief Returns true if the method with the given ID is protected - */ - bool is_protected (size_t mid) const - { - return m_table [mid - m_method_offset].is_protected (); - } - - /** - * @brief Returns the name of the method with the given ID - */ - const std::string &name (size_t mid) const - { - return m_table [mid - m_method_offset].name (); - } - - /** - * @brief Returns the name of the property with the given ID - */ - const std::string &property_name (size_t mid) const - { - return m_property_table [mid - m_property_offset].first.name (); - } - - /** - * @brief Begins iteration of the overload variants for setter of property ID mid - */ - MethodTableEntry::method_iterator begin_setters (size_t mid) const - { - return m_property_table[mid - m_property_offset].first.begin (); - } - - /** - * @brief Ends iteration of the overload variants for setter of property ID mid - */ - MethodTableEntry::method_iterator end_setters (size_t mid) const - { - return m_property_table[mid - m_property_offset].first.end (); - } - - /** - * @brief Begins iteration of the overload variants for getter of property ID mid - */ - MethodTableEntry::method_iterator begin_getters (size_t mid) const - { - return m_property_table[mid - m_property_offset].second.begin (); - } - - /** - * @brief Ends iteration of the overload variants for getter of property ID mid - */ - MethodTableEntry::method_iterator end_getters (size_t mid) const - { - return m_property_table[mid - m_property_offset].second.end (); - } - - /** - * @brief Begins iteration of the overload variants for method ID mid - */ - MethodTableEntry::method_iterator begin (size_t mid) const - { - return m_table[mid - m_method_offset].begin (); - } - - /** - * @brief Ends iteration of the overload variants for method ID mid - */ - MethodTableEntry::method_iterator end (size_t mid) const - { - return m_table[mid - m_method_offset].end (); - } - - /** - * @brief Finishes construction of the table - * This method must be called after the add_method calls have been used - * to fill the table. It will remove duplicate entries and clean up memory. - */ - void finish () - { - for (std::vector::iterator m = m_table.begin (); m != m_table.end (); ++m) { - m->finish (); - } - for (std::vector >::iterator m = m_property_table.begin (); m != m_property_table.end (); ++m) { - m->first.finish (); - m->second.finish (); - } - } - - /** - * @brief Obtain a method table for a given class - */ - static MethodTable *method_table_by_class (const gsi::ClassBase *cls_decl); - -private: - size_t m_method_offset; - size_t m_property_offset; - const gsi::ClassBase *mp_cls_decl; - std::map, size_t> m_name_map; - std::map, size_t> m_property_name_map; - std::vector m_table; - std::vector > m_property_table; -}; - -struct PythonClassClientData - : public gsi::PerClassClientSpecificData -{ - 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, bool as_static) - { - PythonClassClientData *cd = dynamic_cast(cls_decl.data (gsi::ClientIndex::Python)); - 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, bool as_static) - { - 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)); - } - } -}; - -/** - * @brief Obtains a method table for a given class - */ -MethodTable *MethodTable::method_table_by_class (const gsi::ClassBase *cls_decl) -{ - PythonClassClientData *cd = dynamic_cast(cls_decl->data (gsi::ClientIndex::Python)); - return cd ? &cd->method_table : 0; -} - // -------------------------------------------------------------------------- // Some utilities @@ -547,7 +137,7 @@ static std::string extract_python_name (const std::string &name) #else return "__truediv__"; #endif - } else if (name == "*") { + } else if (name == "*" || name == "*!") { return "__mul__"; } else if (name == "%") { return "__mod__"; @@ -625,6 +215,573 @@ static std::string extract_python_name (const std::string &name) } } +// ------------------------------------------------------------------- +// The lookup table for the method overload resolution + +/** + * @brief A single entry in the method table + * This class provides an entry for one name. It provides flags + * (ctor, static, protected) for the method and a list of implementations + * (gsi::MethodBase objects) + */ +class MethodTableEntry +{ +public: + typedef std::vector::const_iterator method_iterator; + + MethodTableEntry (const std::string &name, bool st, bool prot) + : m_name (name), m_is_static (st), m_is_protected (prot), m_is_enabled (true) + { } + + const std::string &name () const + { + return m_name; + } + + void set_name (const std::string &n) + { + m_name = n; + } + + void set_enabled (bool en) + { + m_is_enabled = en; + } + + bool is_enabled () const + { + return m_is_enabled; + } + + bool is_static () const + { + return m_is_static; + } + + bool is_protected () const + { + return m_is_protected; + } + + void add (const gsi::MethodBase *m) + { + m_methods.push_back (m); + } + + void finish () + { + // remove duplicate entries in the method list + std::vector m = m_methods; + std::sort(m.begin (), m.end ()); + m_methods.assign (m.begin (), std::unique (m.begin (), m.end ())); + } + + method_iterator begin () const + { + return m_methods.begin (); + } + + method_iterator end () const + { + return m_methods.end (); + } + +private: + std::string m_name; + bool m_is_static : 1; + bool m_is_protected : 1; + bool m_is_enabled : 1; + std::vector m_methods; +}; + +/** + * @brief The method table for a class + * The method table will provide the methods associated with a native method, i.e. + * a certain name. It only provides the methods, not a overload resolution strategy. + */ +class MethodTable +{ +public: + /** + * @brief Constructor + * This constructor will create a method table for the given class and register + * this table under this class. + */ + MethodTable (const gsi::ClassBase *cls_decl, PythonModule *module) + : m_method_offset (0), m_property_offset (0), mp_cls_decl (cls_decl), mp_module (module) + { + if (cls_decl->base ()) { + const MethodTable *base_mt = method_table_by_class (cls_decl->base ()); + tl_assert (base_mt); + m_method_offset = base_mt->top_mid (); + m_property_offset = base_mt->top_property_mid (); + } + + // 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); + } + } + } + } + } + } + + /** + * @brief Returns the lowest method ID within the space of this table + * Method IDs below this one are reserved for base class methods + */ + size_t bottom_mid () const + { + return m_method_offset; + } + + /** + * @brief Returns the topmost + 1 method ID. + */ + size_t top_mid () const + { + return m_method_offset + m_table.size (); + } + + /** + * @brief Returns the lowest property method ID within the space of this table + * Method IDs below this one are reserved for base class methods + */ + size_t bottom_property_mid () const + { + return m_property_offset; + } + + /** + * @brief Returns the topmost + 1 property method ID. + */ + size_t top_property_mid () const + { + return m_property_offset + m_property_table.size (); + } + + /** + * @brief Find a method with the given name and static flag + * Returns true or false in the first part (true, if found) and + * the MID in the second part. + */ + std::pair find_method (bool st, const std::string &name) const + { + std::map , size_t>::const_iterator t = m_name_map.find (std::make_pair (st, name)); + if (t != m_name_map.end ()) { + return std::make_pair (true, t->second + m_method_offset); + } else { + return std::make_pair (false, 0); + } + } + + /** + * @brief Find a property with the given name and static flag + * Returns true or false in the first part (true, if found) and + * the MID in the second part. + */ + std::pair find_property (bool st, const std::string &name) const + { + std::map , size_t>::const_iterator t = m_property_name_map.find (std::make_pair (st, name)); + if (t != m_property_name_map.end ()) { + return std::make_pair (true, t->second + m_property_offset); + } else { + return std::make_pair (false, 0); + } + } + + /** + * @brief Adds a method to the table + */ + void add_method (const std::string &name, const gsi::MethodBase *mb) + { + if (name == "to_s" && mb->compatible_with_num_args (0)) { + + add_method_basic (name, mb); + + // The str method is also routed via the tp_str implementation + add_method_basic ("__str__", mb); + +#if defined(GSI_ALIAS_INSPECT) + // also alias to "__repr__" unless there is an explicit "inspect" method + bool alias_inspect = true; + for (gsi::ClassBase::method_iterator m = mp_cls_decl->begin_methods (); m != mp_cls_decl->end_methods () && alias_inspect; ++m) { + if (! (*m)->is_callback () && ! (*m)->is_signal ()) { + for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms () && alias_inspect; ++syn) { + if (! syn->is_getter && ! syn->is_setter && syn->name == "inspect") { + alias_inspect = false; + } + } + } + } +#else + bool alias_inspect = false; +#endif + if (alias_inspect) { + add_method_basic ("__repr__", mb); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'str(object)' and 'repr(object)'"))); + } else { + mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'str(object)'"))); + } + + } else if (name == "hash" && mb->compatible_with_num_args (0)) { + + // The hash method is also routed via the tp_hash implementation + add_method_basic ("__hash__", mb); + + add_method_basic (name, mb); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'hash(object)'"))); + + } else if (name == "inspect" && mb->compatible_with_num_args (0)) { + + // The str method is also routed via the tp_str implementation + add_method_basic ("__repr__", mb); + + add_method_basic (name, mb); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'repr(object)'"))); + + } else if (name == "size" && mb->compatible_with_num_args (0)) { + + // The size method is also routed via the sequence methods protocol if there + // is a [] function + add_method_basic ("__len__", mb); + + add_method_basic (name, mb); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method is also available as 'len(object)'"))); + + } else if (name == "each" && mb->compatible_with_num_args (0) && mb->ret_type ().is_iter ()) { + + // each makes the object iterable + add_method_basic ("__iter__", mb); + + add_method_basic (name, mb); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method enables iteration of the object"))); + + } else if (name == "dup" && mb->compatible_with_num_args (0)) { + + // If the object supports the dup method, then it is a good + // idea to define the __copy__ method. + add_method_basic ("__copy__", mb); + + add_method_basic (name, mb); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method also implements '__copy__'"))); + + } else { + + std::string py_name = extract_python_name (name); + if (py_name.empty ()) { + + // drop non-standard names + if (tl::verbosity () >= 20) { + tl::warn << tl::to_string (tr ("Class ")) << mp_cls_decl->name () << ": " << tl::to_string (tr ("no Python mapping for method ")) << name; + } + + add_method_basic (name, mb, false); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method is not available for Python"))); + + } else { + + add_method_basic (py_name, mb); + + if (name == "*") { + // Supply a commutative multiplication version unless the operator is "*!" + add_method_basic ("__rmul__", mb); + mp_module->add_python_doc (mb, tl::to_string (tr ("This method also implements '__rmul__'"))); + } + + } + + } + } + + /** + * @brief Adds a setter with the given name + */ + void add_setter (const std::string &name, const gsi::MethodBase *setter) + { + bool st = setter->is_static (); + + std::map, size_t>::iterator n = m_property_name_map.find (std::make_pair (st, name)); + if (n == m_property_name_map.end ()) { + + m_property_name_map.insert (std::make_pair (std::make_pair(st, name), m_property_table.size ())); + m_property_table.push_back (std::make_pair (MethodTableEntry (name, st, false), MethodTableEntry (name, st, false))); + m_property_table.back ().first.add (setter); + + } else { + + m_property_table [n->second].first.add (setter); + + } + } + + /** + * @brief Adds a getter with the given name + */ + void add_getter (const std::string &name, const gsi::MethodBase *getter) + { + bool st = getter->is_static (); + + std::map, size_t>::iterator n = m_property_name_map.find (std::make_pair (st, name)); + if (n == m_property_name_map.end ()) { + + m_property_name_map.insert (std::make_pair (std::make_pair(st, name), m_property_table.size ())); + m_property_table.push_back (std::make_pair (MethodTableEntry (name, st, false), MethodTableEntry (name, st, false))); + m_property_table.back ().second.add (getter); + + } else { + + m_property_table [n->second].second.add (getter); + + } + } + + /** + * @brief Returns true if the method is enabled + */ + bool is_enabled (size_t mid) const + { + return m_table [mid - m_method_offset].is_enabled (); + } + + /** + * @brief Enables or disables a method + */ + void set_enabled (size_t mid, bool en) + { + m_table [mid - m_method_offset].set_enabled (en); + } + + /** + * @brief Returns true if the method with the given ID is static + */ + bool is_static (size_t mid) const + { + return m_table [mid - m_method_offset].is_static (); + } + + /** + * @brief Returns true if the method with the given ID is protected + */ + bool is_protected (size_t mid) const + { + return m_table [mid - m_method_offset].is_protected (); + } + + /** + * @brief Renames a method + */ + void rename (size_t mid, const std::string &new_name) + { + std::string old_name = name (mid); + bool st = is_static (mid); + + m_table [mid - m_method_offset].set_name (new_name); + + auto nm = m_name_map.find (std::make_pair (st, old_name)); + if (nm != m_name_map.end ()) { + m_name_map.erase (nm); + m_name_map.insert (std::make_pair (std::make_pair (st, new_name), mid)); + } + } + + /** + * @brief Returns the name of the method with the given ID + */ + const std::string &name (size_t mid) const + { + return m_table [mid - m_method_offset].name (); + } + + /** + * @brief Returns the name of the property with the given ID + */ + const std::string &property_name (size_t mid) const + { + return m_property_table [mid - m_property_offset].first.name (); + } + + /** + * @brief Begins iteration of the overload variants for setter of property ID mid + */ + MethodTableEntry::method_iterator begin_setters (size_t mid) const + { + return m_property_table[mid - m_property_offset].first.begin (); + } + + /** + * @brief Ends iteration of the overload variants for setter of property ID mid + */ + MethodTableEntry::method_iterator end_setters (size_t mid) const + { + return m_property_table[mid - m_property_offset].first.end (); + } + + /** + * @brief Begins iteration of the overload variants for getter of property ID mid + */ + MethodTableEntry::method_iterator begin_getters (size_t mid) const + { + return m_property_table[mid - m_property_offset].second.begin (); + } + + /** + * @brief Ends iteration of the overload variants for getter of property ID mid + */ + MethodTableEntry::method_iterator end_getters (size_t mid) const + { + return m_property_table[mid - m_property_offset].second.end (); + } + + /** + * @brief Begins iteration of the overload variants for method ID mid + */ + MethodTableEntry::method_iterator begin (size_t mid) const + { + return m_table[mid - m_method_offset].begin (); + } + + /** + * @brief Ends iteration of the overload variants for method ID mid + */ + MethodTableEntry::method_iterator end (size_t mid) const + { + return m_table[mid - m_method_offset].end (); + } + + /** + * @brief Finishes construction of the table + * This method must be called after the add_method calls have been used + * to fill the table. It will remove duplicate entries and clean up memory. + */ + void finish () + { + for (std::vector::iterator m = m_table.begin (); m != m_table.end (); ++m) { + m->finish (); + } + for (std::vector >::iterator m = m_property_table.begin (); m != m_property_table.end (); ++m) { + m->first.finish (); + m->second.finish (); + } + } + + /** + * @brief Obtain a method table for a given class + */ + static MethodTable *method_table_by_class (const gsi::ClassBase *cls_decl); + +private: + size_t m_method_offset; + size_t m_property_offset; + const gsi::ClassBase *mp_cls_decl; + std::map, size_t> m_name_map; + std::map, size_t> m_property_name_map; + std::vector m_table; + std::vector > m_property_table; + PythonModule *mp_module; + + void add_method_basic (const std::string &name, const gsi::MethodBase *mb, bool enabled = true) + { + bool st = mb->is_static (); + + std::map, size_t>::iterator n = m_name_map.find (std::make_pair (st, name)); + if (n == m_name_map.end ()) { + + m_name_map.insert (std::make_pair (std::make_pair (st, name), m_table.size ())); + m_table.push_back (MethodTableEntry (name, st, mb->is_protected ())); + if (! enabled) { + m_table.back ().set_enabled (false); + } + m_table.back ().add (mb); + + } else { + + if (m_table [n->second].is_protected () != mb->is_protected ()) { + tl::warn << "Class " << mp_cls_decl->name () << ": method '" << name << " is both a protected and non-protected"; + } + + m_table [n->second].add (mb); + if (! enabled) { + m_table [n->second].set_enabled (false); + } + + } + } +}; + +struct PythonClassClientData + : public gsi::PerClassClientSpecificData +{ + PythonClassClientData (const gsi::ClassBase *_cls, PyTypeObject *_py_type, PyTypeObject *_py_type_static, PythonModule *module) + : py_type_object (_py_type), py_type_object_static (_py_type_static), method_table (_cls, module) + { + // .. nothing yet .. + } + + PyTypeObject *py_type_object; + PyTypeObject *py_type_object_static; + MethodTable method_table; + + static PyTypeObject *py_type (const gsi::ClassBase &cls_decl, bool as_static) + { + PythonClassClientData *cd = dynamic_cast(cls_decl.data (gsi::ClientIndex::Python)); + 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, bool as_static, PythonModule *module) + { + 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, module)); + } + } +}; + +/** + * @brief Obtains a method table for a given class + */ +MethodTable *MethodTable::method_table_by_class (const gsi::ClassBase *cls_decl) +{ + PythonClassClientData *cd = dynamic_cast(cls_decl->data (gsi::ClientIndex::Python)); + return cd ? &cd->method_table : 0; +} + // -------------------------------------------------------------------------- // Methods for PYAObjectBase Python binding @@ -2489,7 +2646,7 @@ public: type->tp_getattro = PyObject_GenericGetAttr; } - PythonClassClientData::initialize (*cls, type, as_static); + PythonClassClientData::initialize (*cls, type, as_static, mp_module); mp_module->register_class (cls); @@ -2614,325 +2771,227 @@ public: // 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) { + if (! mt->is_enabled (mid)) { + continue; + } + std::string name = mt->name (mid); + std::string raw_name = name; - // extract a suitable Python name - name = extract_python_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 += "_"; + } - // cannot extract a Python name - if (name.empty ()) { + // 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; + } + + mp_module->add_python_doc (*cls, mt, int (mid), tl::sprintf (tl::to_string (tr ("This attribute is available as '%s' in Python")), 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 ")) << mt->name (mid); + tl::warn << tl::to_string (tr ("Class ")) << cls->name () << ": " << tl::to_string (tr ("no Python mapping for method (reserved word) ")) << name; } - mp_module->add_python_doc (*cls, mt, int (mid), tl::to_string (tr ("This method is not available for Python"))); + name += "_"; - } else { + mt->rename (mid, 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)); - 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 += "_"; + // 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 (); + } - // needs static/non-static disambiguation? - t = mt->find_method (! mt->is_static (mid), name); - if (t.first) { + const gsi::MethodBase *m_first = *mt->begin (mid); - disambiguated_names.push_back (name); - if (mt->is_static (mid)) { - name = "_class_" + name; - } else { - name = "_inst_" + name; - } + tl_assert (mid < sizeof (method_adaptors) / sizeof (method_adaptors[0])); + if (! mt->is_static (mid)) { // Bound methods - } 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)) { // Bound methods - - 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 - // But this should only apply if the multiplication is commutative - // There are a few exceptions like Trans * Trans, so we support this case only if - // the second argument is double (scaling). - gsi::ArgType double_argtype; - double_argtype.template init (); - if (m_first->arg (0) == double_argtype) { - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method is also available as '__rmul__'"))); - alt_names.push_back ("__rmul__"); - } - - } else if (name == "dup" && m_first->compatible_with_num_args (0) ) { - - // If the object supports the dup method, then it is a good - // idea to define the __copy__ method. - add_python_doc (**c, mt, int (mid), tl::to_string (tr ("This method also implements '__copy__'"))); - alt_names.push_back ("__copy__"); - - } - - 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) { // Class methods - - 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); - - } + if (! as_static) { 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; + method->ml_flags = METH_VARARGS; - PythonRef attr = PythonRef (PyDescr_NewClassMethod (type, method)); + 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) { // Class methods + + 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" + if (! as_static) { - 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; + // 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" - if (! has_cmp && has_eq) { + 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_ne) { + if (! has_cmp && has_eq) { - // 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; + if (! has_ne) { - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + // 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; + if (has_lt && ! has_le) { - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + // 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; + if (has_lt && ! has_gt) { - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + // 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; + if (has_lt && ! has_ge) { - PythonRef attr = PythonRef (PyDescr_NewMethod (type, method)); - set_type_attr (type, method->ml_name, attr); + // 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) { + // install the static/non-static dispatcher descriptor - 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) { + for (std::vector::const_iterator a = disambiguated_names.begin (); a != disambiguated_names.end (); ++a) { - // some error -> don't install the disambiguator - Py_XDECREF (attr_inst); - Py_XDECREF (attr_class); - PyErr_Clear (); + 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) { - 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); + // some error -> don't install the disambiguator + Py_XDECREF (attr_inst); + Py_XDECREF (attr_class); + PyErr_Clear (); + if (tl::verbosity () >= 20) { + tl::warn << "Unable to install a static/non-static disambiguator for " << *a << " in class " << cls->name (); } + } else { + + PyObject *desc = PYAAmbiguousMethodDispatcher::create (attr_inst, attr_class); + PythonRef name (c2python (*a)); + // Note: we use GenericSetAttr since that one allows us setting attributes on built-in types + PyObject_GenericSetAttr ((PyObject *) type, name.get (), desc); + } } diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 051ddac7a..c4099cc0a 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1556,6 +1556,17 @@ rba_add_path (const std::string &path) } } +static std::string +ruby_name (const std::string &n) +{ + if (n == "*!") { + // non-commutative multiplication + return "*"; + } else { + return n; + } +} + namespace { @@ -1672,7 +1683,7 @@ public: } else if (syn->is_setter) { mt->add_method (syn->name + "=", *m); } else { - mt->add_method (syn->name, *m); + mt->add_method (ruby_name (syn->name), *m); } } @@ -1709,7 +1720,7 @@ public: } else { - mt->add_method (syn->name, *m); + mt->add_method (ruby_name (syn->name), *m); } }