diff --git a/src/gsi/gsi/gsiClass.h b/src/gsi/gsi/gsiClass.h index edad9e75d..e116d54bb 100644 --- a/src/gsi/gsi/gsiClass.h +++ b/src/gsi/gsi/gsiClass.h @@ -30,6 +30,7 @@ #include "tlTypeTraits.h" #include "tlHeap.h" #include "tlUtils.h" +#include "tlVariant.h" #include @@ -76,7 +77,7 @@ struct _var_user_less_impl }; /** - * @brief A helper function to implement equal as efficiently as possible + * @brief A helper function to implement to_string as efficiently as possible */ template struct _var_user_to_string_impl; @@ -92,6 +93,57 @@ struct _var_user_to_string_impl static std::string call (const T *a, const VariantUserClassImpl *delegate) { return delegate->to_string_impl ((void *) a); } }; +/** + * @brief A helper function to implement to_variant as efficiently as possible + */ +template struct _var_user_to_variant_impl; + +template +struct _var_user_to_variant_impl +{ + static tl::Variant call (const T *a, const VariantUserClassImpl * /*delegate*/) { return a->to_variant (); } +}; + +template +struct _var_user_to_variant_impl +{ + static tl::Variant call (const T *a, const VariantUserClassImpl *delegate) { return delegate->to_variant_impl ((void *) a); } +}; + +/** + * @brief A helper function to implement to_int as efficiently as possible + */ +template struct _var_user_to_int_impl; + +template +struct _var_user_to_int_impl +{ + static int call (const T *a, const VariantUserClassImpl * /*delegate*/) { return a->to_int (); } +}; + +template +struct _var_user_to_int_impl +{ + static int call (const T *a, const VariantUserClassImpl *delegate) { return delegate->to_int_impl ((void *) a); } +}; + +/** + * @brief A helper function to implement to_double as efficiently as possible + */ +template struct _var_user_to_double_impl; + +template +struct _var_user_to_double_impl +{ + static double call (const T *a, const VariantUserClassImpl * /*delegate*/) { return a->to_double (); } +}; + +template +struct _var_user_to_double_impl +{ + static double call (const T *a, const VariantUserClassImpl *delegate) { return delegate->to_double_impl ((void *) a); } +}; + /** * @brief A VariantUserClassBase specialization that links GSI classes and Variant classes */ @@ -152,11 +204,26 @@ public: return gsi::_var_user_less_impl::value>::call ((const T *) a, (const T *) b, this); } + virtual void to_variant (const void *a, tl::Variant &var) const + { + var = gsi::_var_user_to_variant_impl::value>::call ((const T *) a, this); + } + virtual std::string to_string (const void *a) const { return gsi::_var_user_to_string_impl::value>::call ((const T *) a, this); } + virtual int to_int (const void *a) const + { + return gsi::_var_user_to_int_impl::value>::call ((const T *) a, this); + } + + virtual double to_double (const void *a) const + { + return gsi::_var_user_to_double_impl::value>::call ((const T *) a, this); + } + void *clone (const void *obj) const { void *new_obj = mp_cls->create (); diff --git a/src/gsi/gsi/gsiEnums.h b/src/gsi/gsi/gsiEnums.h index acbee7079..457bb305a 100644 --- a/src/gsi/gsi/gsiEnums.h +++ b/src/gsi/gsi/gsiEnums.h @@ -26,6 +26,7 @@ #include "gsiDecl.h" #include "tlString.h" +#include "tlVariant.h" #if defined(HAVE_QT) # include @@ -60,6 +61,16 @@ public: E &value () { return m_e; } const E &value () const { return m_e; } + int to_int () const { return int (m_e); } + tl::Variant to_variant () const { return tl::Variant (int (m_e)); } + + std::string to_string () const + { + const Enum *ecls = dynamic_cast *> (cls_decl ()); + tl_assert (ecls != 0); + return ecls->enum_to_string (m_e); + } + private: E m_e; }; @@ -209,7 +220,7 @@ public: return ecls->enum_to_string_inspect (*e); } - static int enum_to_i (const E *e) + static int enum_to_int (const E *e) { return int (*e); } @@ -219,16 +230,31 @@ public: return *e == other; } - static bool enum_ne (const E *e, const E &other) + static bool enum_eq_with_int (const E *e, int other) + { + return int (*e) == other; + } + + static bool enum_ne (const E *e, const E &other) { return *e != other; } - static bool enum_lt (const E *e, const E &other) + static bool enum_ne_with_int (const E *e, int other) + { + return int (*e) != other; + } + + static bool enum_lt (const E *e, const E &other) { return *e < other; } + static bool enum_lt_with_int (const E *e, int other) + { + return int (*e) < other; + } + static E *new_enum_from_int (int i) { return new E (E (i)); @@ -248,10 +274,13 @@ public: gsi::constructor ("new", &new_enum_from_string, gsi::arg("s"), "@brief Creates an enum from a string value") + gsi::method_ext ("to_s", &enum_to_string_ext, "@brief Gets the symbolic string from an enum") + gsi::method_ext ("inspect", &enum_to_string_inspect_ext, "@brief Converts an enum to a visual string") + - gsi::method_ext ("to_i", &enum_to_i, "@brief Gets the integer value from the enum") + + gsi::method_ext ("to_i", &enum_to_int, "@brief Gets the integer value from the enum") + gsi::method_ext ("==", &enum_eq, gsi::arg("other"), "@brief Compares two enums") + + gsi::method_ext ("==", &enum_eq_with_int, gsi::arg("other"), "@brief Compares an enum with an integer value") + gsi::method_ext ("!=", &enum_ne, gsi::arg("other"), "@brief Compares two enums for inequality") + - gsi::method_ext ("<", &enum_lt, gsi::arg("other"), "@brief Returns true if the first enum is less (in the enum symbol order) than the second"); + gsi::method_ext ("!=", &enum_ne_with_int, gsi::arg("other"), "@brief Compares an enum with an integer for inequality") + + gsi::method_ext ("<", &enum_lt, gsi::arg("other"), "@brief Returns true if the first enum is less (in the enum symbol order) than the second") + + gsi::method_ext ("<", &enum_lt_with_int, gsi::arg("other"), "@brief Returns true if the enum is less (in the enum symbol order) than the integer value"); return m + defs (); } diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index b6e534342..abda57030 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -1241,6 +1241,96 @@ VariantUserClassImpl::to_string_impl (void *obj) const } } +tl::Variant +VariantUserClassImpl::to_variant_impl (void *obj) const +{ + if (obj) { + + if (! has_method ("to_v")) { + + // no method to convert the object to a string + return tl::Variant (); + + } else { + + tl::ExpressionParserContext context; + + tl::Variant out; + + tl::Variant object (obj, mp_object_cls, false); + std::vector vv; + + execute_gsi (context, out, object, "to_v", vv); + + return out; + + } + + } else { + return tl::Variant (); + } +} + +int +VariantUserClassImpl::to_int_impl (void *obj) const +{ + if (obj) { + + if (! has_method ("to_i")) { + + // no method to convert the object to an integer + return 0; + + } else { + + tl::ExpressionParserContext context; + + tl::Variant out; + + tl::Variant object (obj, mp_object_cls, false); + std::vector vv; + + execute_gsi (context, out, object, "to_i", vv); + + return out.to_int (); + + } + + } else { + return 0; + } +} + +double +VariantUserClassImpl::to_double_impl (void *obj) const +{ + if (obj) { + + if (! has_method ("to_f")) { + + // no method to convert the object to a double value + return 0.0; + + } else { + + tl::ExpressionParserContext context; + + tl::Variant out; + + tl::Variant object (obj, mp_object_cls, false); + std::vector vv; + + execute_gsi (context, out, object, "to_f", vv); + + return out.to_double (); + + } + + } else { + return 0.0; + } +} + void VariantUserClassImpl::execute (const tl::ExpressionParserContext &context, tl::Variant &out, tl::Variant &object, const std::string &method, const std::vector &args) const { diff --git a/src/gsi/gsi/gsiExpression.h b/src/gsi/gsi/gsiExpression.h index d422999d9..d0ff096d4 100644 --- a/src/gsi/gsi/gsiExpression.h +++ b/src/gsi/gsi/gsiExpression.h @@ -50,7 +50,10 @@ public: bool equal_impl (void *, void *) const; bool less_impl (void *, void *) const; + tl::Variant to_variant_impl (void *) const; std::string to_string_impl (void *) const; + int to_int_impl (void *) const; + double to_double_impl (void *) const; virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, tl::Variant &object, const std::string &method, const std::vector &args) const; diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc index 856e44fbb..e5bf11472 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc @@ -1984,6 +1984,9 @@ static std::string get_technology (const lay::CellViewRef *cv) static tl::Event &get_technology_changed_event (lay::CellViewRef *cv) { + if (! cv->is_valid ()) { + throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cellview"))); + } return (*cv)->technology_changed_event; } diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 54f81d9e5..0f962fa7e 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -632,6 +632,14 @@ static std::string extract_python_name (const std::string &name) static void pya_object_deallocate (PyObject *self) { + // This avoids an assertion in debug builds (Python, gcmodule.c - update_refs). + // In short, the GC expects not to see objects with refcount 0 and asserts. + // However, due to triggering of signals or similar, the destructor call below + // may trigger a GC (https://github.com/KLayout/klayout/issues/1054). + // According to the comments this may be turned into a release mode assertion, so + // we better work around it. + ++self->ob_refcnt; + PYAObjectBase *p = PYAObjectBase::from_pyobject (self); p->~PYAObjectBase (); Py_TYPE (self)->tp_free (self); diff --git a/src/tl/tl/tlTypeTraits.h b/src/tl/tl/tlTypeTraits.h index 9f94978f6..491907dc5 100644 --- a/src/tl/tl/tlTypeTraits.h +++ b/src/tl/tl/tlTypeTraits.h @@ -71,6 +71,18 @@ inline bool value_of (false_tag) { return false; } typedef char __yes_type [1]; typedef char __no_type [2]; +/** + * @brief Detects whether a class has a "to_variant" method with a matching signature + */ +template static __yes_type &__test_to_variant_func (decltype (&T::to_variant)); +template static __no_type &__test_to_variant_func (...); + +template +struct has_to_variant +{ + static constexpr bool value = sizeof (__test_to_variant_func (nullptr)) == sizeof (__yes_type); +}; + /** * @brief Detects whether a class has a "to_string" method with a matching signature */ @@ -83,6 +95,30 @@ struct has_to_string static constexpr bool value = sizeof (__test_to_string_func (nullptr)) == sizeof (__yes_type); }; +/** + * @brief Detects whether a class has a "to_int" method with a matching signature + */ +template static __yes_type &__test_to_int_func (decltype (&T::to_int)); +template static __no_type &__test_to_int_func (...); + +template +struct has_to_int +{ + static constexpr bool value = sizeof (__test_to_int_func (nullptr)) == sizeof (__yes_type); +}; + +/** + * @brief Detects whether a class has a "to_double" method with a matching signature + */ +template static __yes_type &__test_to_double_func (decltype (&T::to_double)); +template static __no_type &__test_to_double_func (...); + +template +struct has_to_double +{ + static constexpr bool value = sizeof (__test_to_double_func (nullptr)) == sizeof (__yes_type); +}; + /** * @brief Detects whether a class has an equal operator */ diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index 72dd3c32d..7e46e0add 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -1820,6 +1820,10 @@ Variant::to_int128 () const __int128 l = 0; ex.read (l); return l; + } else if (m_type == t_user) { + return m_var.mp_user.cls->to_int (m_var.mp_user.object); + } else if (m_type == t_user_ref) { + return m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast (m_var.mp_user_ref.ptr)->get ())); } else { return 0; } @@ -1875,6 +1879,10 @@ Variant::to_ulonglong () const unsigned long long l = 0; tl::from_string (to_string (), l); return l; + } else if (m_type == t_user) { + return (unsigned long long) m_var.mp_user.cls->to_int (m_var.mp_user.object); + } else if (m_type == t_user_ref) { + return (unsigned long long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast (m_var.mp_user_ref.ptr)->get ())); } else { return 0; } @@ -1929,6 +1937,10 @@ Variant::to_longlong () const long long l = 0; tl::from_string (to_string (), l); return l; + } else if (m_type == t_user) { + return (long long) m_var.mp_user.cls->to_int (m_var.mp_user.object); + } else if (m_type == t_user_ref) { + return (long long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast (m_var.mp_user_ref.ptr)->get ())); } else { return 0; } @@ -1983,6 +1995,10 @@ Variant::to_ulong () const unsigned long l = 0; tl::from_string (to_string (), l); return l; + } else if (m_type == t_user) { + return (unsigned long) m_var.mp_user.cls->to_int (m_var.mp_user.object); + } else if (m_type == t_user_ref) { + return (unsigned long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast (m_var.mp_user_ref.ptr)->get ())); } else { return 0; } @@ -2037,6 +2053,10 @@ Variant::to_long () const long l = 0; tl::from_string (to_string (), l); return l; + } else if (m_type == t_user) { + return (long) m_var.mp_user.cls->to_int (m_var.mp_user.object); + } else if (m_type == t_user_ref) { + return (long) m_var.mp_user_ref.cls->to_int (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast (m_var.mp_user_ref.ptr)->get ())); } else { return 0; } @@ -2143,6 +2163,10 @@ Variant::to_double () const double d = 0; tl::from_string (to_string (), d); return d; + } else if (m_type == t_user) { + return m_var.mp_user.cls->to_double (m_var.mp_user.object); + } else if (m_type == t_user_ref) { + return m_var.mp_user_ref.cls->to_double (m_var.mp_user_ref.cls->deref_proxy_const (reinterpret_cast (m_var.mp_user_ref.ptr)->get ())); } else { return 0; } @@ -2513,7 +2537,9 @@ QVariant Variant::to_qvariant () const } else if (dynamic_cast *> (cls)) { return QVariant (to_user ()); } else { - return QVariant (); + tl::Variant var; + cls->to_variant (to_user (), var); + return var.to_qvariant (); } } default: diff --git a/src/tl/tl/tlVariant.h b/src/tl/tl/tlVariant.h index d50b33be5..14a388dea 100644 --- a/src/tl/tl/tlVariant.h +++ b/src/tl/tl/tlVariant.h @@ -53,6 +53,7 @@ namespace gsi namespace tl { +class Variant; class Extractor; class EvalClass; template void extractor_impl (tl::Extractor &, T &); @@ -76,6 +77,9 @@ public: virtual bool less (const void *, const void *) const = 0; virtual void *clone (const void *) const = 0; virtual std::string to_string (const void *) const = 0; + virtual int to_int (const void *) const = 0; + virtual double to_double (const void *) const = 0; + virtual void to_variant (const void *, tl::Variant &var) const = 0; virtual void read (void *, tl::Extractor &ex) const = 0; virtual const char *name () const = 0; virtual bool is_const () const = 0; diff --git a/src/tl/tl/tlVariantUserClasses.h b/src/tl/tl/tlVariantUserClasses.h index 4596eac5e..db27a852c 100644 --- a/src/tl/tl/tlVariantUserClasses.h +++ b/src/tl/tl/tlVariantUserClasses.h @@ -119,6 +119,57 @@ struct _var_user_to_string_impl static std::string call (const T *) { tl_assert (false); } }; +/** + * @brief A helper function to implement equal as efficiently as possible + */ +template struct _var_user_to_int_impl; + +template +struct _var_user_to_int_impl +{ + static int call (const T *a) { return a->to_int (); } +}; + +template +struct _var_user_to_int_impl +{ + static int call (const T *) { tl_assert (false); } +}; + +/** + * @brief A helper function to implement equal as efficiently as possible + */ +template struct _var_user_to_double_impl; + +template +struct _var_user_to_double_impl +{ + static double call (const T *a) { return a->to_double (); } +}; + +template +struct _var_user_to_double_impl +{ + static double call (const T *) { tl_assert (false); } +}; + +/** + * @brief A helper function to implement equal as efficiently as possible + */ +template struct _var_user_to_variant_impl; + +template +struct _var_user_to_variant_impl +{ + static tl::Variant call (const T *a) { return a->to_variant (); } +}; + +template +struct _var_user_to_variant_impl +{ + static tl::Variant call (const T *) { tl_assert (false); } +}; + /** * @brief A utility implementation of tl::VariantUserClass using type traits for the implementation */ @@ -162,7 +213,22 @@ public: return _var_user_to_string_impl::value>::call ((const T *) a); } - virtual void read (void *a, tl::Extractor &ex) const + virtual int to_int (const void *a) const + { + return _var_user_to_int_impl::value>::call ((const T *) a); + } + + virtual double to_double (const void *a) const + { + return _var_user_to_double_impl::value>::call ((const T *) a); + } + + virtual void to_variant (const void *a, tl::Variant &v) const + { + v = _var_user_to_variant_impl::value>::call ((const T *) a); + } + + virtual void read (void *a, tl::Extractor &ex) const { ex.read (*(T *)a); } diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index 6cd4fb4c0..b25c54c44 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -454,6 +454,9 @@ public: virtual void *clone (const void *) const { tl_assert (false); } virtual void assign (void *, const void *) const { tl_assert (false); } virtual std::string to_string (const void *) const { tl_assert (false); } + virtual int to_int (const void *) const { tl_assert (false); } + virtual double to_double (const void *) const { tl_assert (false); } + virtual void to_variant (const void *, tl::Variant &) const { tl_assert (false); } virtual void read (void *, tl::Extractor &) const { } virtual const char *name () const { return "Box"; } virtual unsigned int type_code () const { return 0; } @@ -519,6 +522,9 @@ public: virtual void *clone (const void *) const { tl_assert (false); } virtual void assign (void *, const void *) const { tl_assert (false); } virtual std::string to_string (const void *) const { tl_assert (false); } + virtual int to_int (const void *) const { tl_assert (false); } + virtual double to_double (const void *) const { tl_assert (false); } + virtual void to_variant (const void *, tl::Variant &) const { tl_assert (false); } virtual void read (void *, tl::Extractor &) const { } virtual const char *name () const { return "Edge"; } virtual unsigned int type_code () const { return 0; }