mirror of https://github.com/KLayout/klayout.git
Generic GSI methods #_to_const_object (for testing) and #_const_cast. Fixed a class initialization issue - sub classes should not be registered by name at top level in Expressions
This commit is contained in:
parent
82b3030352
commit
fd1dc842e0
|
|
@ -353,6 +353,48 @@ sm_is_const (const char *name)
|
|||
return sm;
|
||||
}
|
||||
|
||||
static SpecialMethod *
|
||||
sm_to_const (const char *name, const gsi::ClassBase *cls)
|
||||
{
|
||||
SpecialMethod *sm = new SpecialMethod (name,
|
||||
tl::to_string (tr ("@hide")), // provided for test purposes mainly
|
||||
true, // const
|
||||
false, // non-static
|
||||
MethodBase::ToConst);
|
||||
|
||||
gsi::ArgType ret;
|
||||
ret.set_is_cptr (true);
|
||||
ret.set_type (gsi::T_object);
|
||||
ret.set_pass_obj (false);
|
||||
ret.set_cls (cls);
|
||||
sm->set_return (ret);
|
||||
|
||||
return sm;
|
||||
}
|
||||
|
||||
static SpecialMethod *
|
||||
sm_const_cast (const char *name, const gsi::ClassBase *cls)
|
||||
{
|
||||
SpecialMethod *sm = new SpecialMethod (name,
|
||||
tl::to_string (tr ("@brief Returns a non-const reference to self.\n"
|
||||
"Basically, this method allows turning a const object reference to a non-const one. "
|
||||
"This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.29.6.")),
|
||||
true, // const
|
||||
false, // non-static
|
||||
MethodBase::ConstCast);
|
||||
|
||||
gsi::ArgType ret;
|
||||
ret.set_is_ptr (true);
|
||||
ret.set_type (gsi::T_object);
|
||||
ret.set_pass_obj (false);
|
||||
ret.set_cls (cls);
|
||||
sm->set_return (ret);
|
||||
|
||||
return sm;
|
||||
}
|
||||
|
||||
static SpecialMethod *
|
||||
sm_destroyed (const char *name)
|
||||
{
|
||||
|
|
@ -598,6 +640,9 @@ ClassBase::merge_declarations ()
|
|||
non_const_decl->add_method (sm_is_const ("_is_const_object?"));
|
||||
}
|
||||
|
||||
non_const_decl->add_method (sm_to_const ("_to_const_object", &*c));
|
||||
non_const_decl->add_method (sm_const_cast ("_const_cast", &*c));
|
||||
|
||||
}
|
||||
|
||||
// finally merge the new classes into the existing ones
|
||||
|
|
|
|||
|
|
@ -305,13 +305,17 @@ initialize_expressions ()
|
|||
// install the method table:
|
||||
ExpressionMethodTable::initialize_class (*c);
|
||||
|
||||
// register a function that creates a class object (use a function to avoid issues with
|
||||
// late destruction of global variables which the class object is already gone)
|
||||
const tl::VariantUserClassBase *cc = (*c)->var_cls_cls ();
|
||||
if (cc) {
|
||||
tl::Eval::define_global_function ((*c)->name (), new EvalClassFunction (cc));
|
||||
}
|
||||
// Note: skip non-top-level classes
|
||||
if ((*c)->parent () == 0) {
|
||||
|
||||
// register a function that creates a class object (use a function to avoid issues with
|
||||
// late destruction of global variables which the class object is already gone)
|
||||
const tl::VariantUserClassBase *cc = (*c)->var_cls_cls ();
|
||||
if (cc) {
|
||||
tl::Eval::define_global_function ((*c)->name (), new EvalClassFunction (cc));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -630,6 +634,14 @@ special_method_impl (gsi::MethodBase::special_method_type smt, tl::Variant &self
|
|||
// nothing to do here for GSI objects
|
||||
} else if (smt == gsi::MethodBase::IsConst) {
|
||||
return tl::Variant (self.user_is_const ());
|
||||
} else if (smt == gsi::MethodBase::ToConst) {
|
||||
tl::Variant res (self);
|
||||
res.user_change_constness (true);
|
||||
return res;
|
||||
} else if (smt == gsi::MethodBase::ConstCast) {
|
||||
tl::Variant res (self);
|
||||
res.user_change_constness (false);
|
||||
return res;
|
||||
} else if (smt == gsi::MethodBase::Destroyed) {
|
||||
|
||||
if (self.type_code () == tl::Variant::t_user) {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,9 @@ public:
|
|||
Destroy,
|
||||
Create,
|
||||
IsConst,
|
||||
Destroyed,
|
||||
ConstCast,
|
||||
ToConst,
|
||||
Destroyed,
|
||||
Assign,
|
||||
Dup
|
||||
};
|
||||
|
|
|
|||
|
|
@ -829,3 +829,37 @@ TEST(15)
|
|||
v = e.parse("var bb = BB.new; bb.d4(1, 'a', 2.0, BB.E.E3B, 42)").execute();
|
||||
EXPECT_EQ (v.to_string (), "1,a,2,101,42");
|
||||
}
|
||||
|
||||
// constness
|
||||
TEST(16)
|
||||
{
|
||||
tl::Eval e;
|
||||
tl::Variant v;
|
||||
v = e.parse ("var b=B.new(); b._is_const_object").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("false"));
|
||||
try {
|
||||
v = e.parse ("var b=B.new(); var bc=b._to_const_object; bc.set_str('abc')").execute ();
|
||||
EXPECT_EQ (1, 0);
|
||||
} catch (tl::Exception &ex) {
|
||||
EXPECT_EQ (ex.msg (), "Cannot call non-const method set_str, class B on a const reference at position 44 (...set_str('abc'))");
|
||||
}
|
||||
v = e.parse ("var e=E.new(); var ec=e.dup; [e._is_const_object, ec._to_const_object._is_const_object]").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("false,true"));
|
||||
v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; [e.x, ec.x]").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("17,17"));
|
||||
v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec._is_const_object").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("true"));
|
||||
v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec=ec._const_cast; ec._is_const_object").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("false"));
|
||||
v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec=ec._const_cast; ec.x=42; e.x").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("42"));
|
||||
v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; ec.x").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("17"));
|
||||
try {
|
||||
v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; e._destroy; ec.x").execute ();
|
||||
EXPECT_EQ (1, 0);
|
||||
} catch (tl::Exception &ex) {
|
||||
EXPECT_EQ (ex.msg (), "Object has been destroyed already at position 64 (...x)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -721,6 +721,50 @@ object_is_const (PyObject *self, PyObject *args)
|
|||
return c2python (PYAObjectBase::from_pyobject (self)->const_ref ());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implements to_const and const_cast
|
||||
*/
|
||||
static PyObject *
|
||||
object_change_const (PyObject *self, PyObject *args, bool to_const)
|
||||
{
|
||||
if (PYAObjectBase::from_pyobject (self)->const_ref () == to_const) {
|
||||
return self;
|
||||
}
|
||||
|
||||
const gsi::ClassBase *cls_decl_self = PythonModule::cls_for_type (Py_TYPE (self));
|
||||
tl_assert (cls_decl_self != 0);
|
||||
|
||||
if (! PyArg_ParseTuple (args, "")) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *new_object = Py_TYPE (self)->tp_alloc (Py_TYPE (self), 0);
|
||||
PythonRef obj (new_object);
|
||||
PYAObjectBase *new_pya_base = PYAObjectBase::from_pyobject_unsafe (new_object);
|
||||
new (new_pya_base) PYAObjectBase (cls_decl_self, new_object);
|
||||
new_pya_base->set (PYAObjectBase::from_pyobject (self)->obj (), false, to_const, false);
|
||||
|
||||
return obj.release ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implements to_const
|
||||
*/
|
||||
static PyObject *
|
||||
object_to_const (PyObject *self, PyObject *args)
|
||||
{
|
||||
return object_change_const (self, args, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Implements const_cast
|
||||
*/
|
||||
static PyObject *
|
||||
object_const_cast (PyObject *self, PyObject *args)
|
||||
{
|
||||
return object_change_const (self, args, false);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, PyObject *args)
|
||||
{
|
||||
|
|
@ -734,6 +778,10 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P
|
|||
return object_create (self, args);
|
||||
} else if (smt == gsi::MethodBase::IsConst) {
|
||||
return object_is_const (self, args);
|
||||
} else if (smt == gsi::MethodBase::ToConst) {
|
||||
return object_to_const (self, args);
|
||||
} else if (smt == gsi::MethodBase::ConstCast) {
|
||||
return object_const_cast (self, args);
|
||||
} else if (smt == gsi::MethodBase::Destroyed) {
|
||||
return object_destroyed (self, args);
|
||||
} else if (smt == gsi::MethodBase::Assign) {
|
||||
|
|
|
|||
|
|
@ -913,6 +913,16 @@ handle_exception (const std::string &where)
|
|||
handle_exception ((where)); \
|
||||
}
|
||||
|
||||
static void free_proxy (void *p)
|
||||
{
|
||||
delete ((Proxy *) p);
|
||||
}
|
||||
|
||||
static void mark_proxy (void *p)
|
||||
{
|
||||
((Proxy *) p)->mark ();
|
||||
}
|
||||
|
||||
static VALUE
|
||||
destroy (VALUE self)
|
||||
{
|
||||
|
|
@ -971,6 +981,37 @@ is_const (VALUE self)
|
|||
return c2ruby<bool> (p->const_ref ());
|
||||
}
|
||||
|
||||
static VALUE
|
||||
to_const (VALUE self)
|
||||
{
|
||||
Proxy *p = 0;
|
||||
Data_Get_Struct (self, Proxy, p);
|
||||
if (! p->const_ref ()) {
|
||||
// promote to const object
|
||||
// NOTE: there is only ONE instance we're going to change this instance
|
||||
// to const here. This has a global effect and this is the reason why this
|
||||
// method is not public. It is provided for testing purposes mainly.
|
||||
p->set_const_ref (true);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
const_cast_ (VALUE self)
|
||||
{
|
||||
Proxy *p = 0;
|
||||
Data_Get_Struct (self, Proxy, p);
|
||||
if (p->const_ref ()) {
|
||||
// promote to non-const object
|
||||
// NOTE: this is a global change of constness and will affect all references
|
||||
// that exist for this object.
|
||||
p->set_const_ref (false);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
assign (VALUE self, VALUE src)
|
||||
{
|
||||
|
|
@ -1024,6 +1065,12 @@ special_method_impl (const gsi::MethodBase *meth, int argc, VALUE *argv, VALUE s
|
|||
} else if (smt == gsi::MethodBase::IsConst) {
|
||||
tl_assert (!ctor);
|
||||
return is_const (self);
|
||||
} else if (smt == gsi::MethodBase::ToConst) {
|
||||
tl_assert (!ctor);
|
||||
return to_const (self);
|
||||
} else if (smt == gsi::MethodBase::ConstCast) {
|
||||
tl_assert (!ctor);
|
||||
return const_cast_ (self);
|
||||
} else if (smt == gsi::MethodBase::Destroyed) {
|
||||
tl_assert (!ctor);
|
||||
return destroyed (self);
|
||||
|
|
@ -1043,16 +1090,6 @@ special_method_impl (const gsi::MethodBase *meth, int argc, VALUE *argv, VALUE s
|
|||
}
|
||||
}
|
||||
|
||||
static void free_proxy (void *p)
|
||||
{
|
||||
delete ((Proxy *) p);
|
||||
}
|
||||
|
||||
static void mark_proxy (void *p)
|
||||
{
|
||||
((Proxy *) p)->mark ();
|
||||
}
|
||||
|
||||
static VALUE alloc_proxy (VALUE klass)
|
||||
{
|
||||
tl_assert (TYPE (klass) == T_CLASS);
|
||||
|
|
|
|||
|
|
@ -3192,6 +3192,17 @@ Eval::set_var (const std::string &name, const tl::Variant &var)
|
|||
m_local_vars.insert (std::make_pair (name, tl::Variant ())).first->second = var;
|
||||
}
|
||||
|
||||
tl::Variant *
|
||||
Eval::var (const std::string &name)
|
||||
{
|
||||
auto f = m_local_vars.find (name);
|
||||
if (f != m_local_vars.end ()) {
|
||||
return &f->second;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Eval::define_function (const std::string &name, EvalFunction *function)
|
||||
{
|
||||
|
|
@ -3202,6 +3213,17 @@ Eval::define_function (const std::string &name, EvalFunction *function)
|
|||
f = function;
|
||||
}
|
||||
|
||||
EvalFunction *
|
||||
Eval::function (const std::string &name)
|
||||
{
|
||||
auto f = m_local_functions.find (name);
|
||||
if (f != m_local_functions.end ()) {
|
||||
return f->second;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Eval::eval_top (ExpressionParserContext &ex, std::unique_ptr<ExpressionNode> &n)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -448,6 +448,12 @@ public:
|
|||
*/
|
||||
void define_function (const std::string &name, EvalFunction *function);
|
||||
|
||||
/**
|
||||
* @brief Gets the function for the given name
|
||||
* Returns 0 if there is no such function.
|
||||
*/
|
||||
EvalFunction *function (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Define a global variable for use within an expression
|
||||
*/
|
||||
|
|
@ -461,6 +467,12 @@ public:
|
|||
*/
|
||||
void set_var (const std::string &name, const tl::Variant &var);
|
||||
|
||||
/**
|
||||
* @brief Gets the function for the given name
|
||||
* Returns 0 if there is no such function.
|
||||
*/
|
||||
tl::Variant *var (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Parse an expression from the extractor
|
||||
*
|
||||
|
|
@ -543,6 +555,30 @@ public:
|
|||
return m_match_substrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the global context
|
||||
*/
|
||||
static tl::Eval &global_context ()
|
||||
{
|
||||
return m_global;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the global context for this context
|
||||
*/
|
||||
tl::Eval *global ()
|
||||
{
|
||||
return mp_global;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the parent context for this context
|
||||
*/
|
||||
tl::Eval *parent ()
|
||||
{
|
||||
return mp_parent;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Expression;
|
||||
|
||||
|
|
|
|||
|
|
@ -2738,6 +2738,17 @@ void *Variant::user_unshare () const
|
|||
return const_cast<void *> (to_user ());
|
||||
}
|
||||
|
||||
void Variant::user_change_constness (bool constness)
|
||||
{
|
||||
tl_assert (is_user ());
|
||||
|
||||
if (m_type == t_user) {
|
||||
m_var.mp_user.cls = m_var.mp_user.cls->change_constness (constness);
|
||||
} else if (m_type == t_user_ref) {
|
||||
m_var.mp_user_ref.cls = m_var.mp_user_ref.cls->change_constness (constness);
|
||||
}
|
||||
}
|
||||
|
||||
void Variant::user_assign (const tl::Variant &other)
|
||||
{
|
||||
tl_assert (is_user ());
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public:
|
|||
virtual const gsi::ClassBase *gsi_cls () const = 0;
|
||||
virtual const tl::EvalClass *eval_cls () const = 0;
|
||||
virtual void *deref_proxy (tl::Object *proxy) const = 0;
|
||||
virtual const tl::VariantUserClassBase *change_constness (bool constness) const = 0;
|
||||
|
||||
const void *deref_proxy_const (const tl::Object *proxy) const
|
||||
{
|
||||
|
|
@ -126,6 +127,11 @@ public:
|
|||
return VariantUserClassBase::instance (typeid (T), is_const);
|
||||
}
|
||||
|
||||
const tl::VariantUserClassBase *change_constness (bool constness) const
|
||||
{
|
||||
return instance (constness);
|
||||
}
|
||||
|
||||
private:
|
||||
static const tl::VariantUserClassBase *ms_instances[4];
|
||||
|
||||
|
|
@ -1001,6 +1007,11 @@ public:
|
|||
*/
|
||||
void *user_unshare () const;
|
||||
|
||||
/**
|
||||
* @brief Changes the constness of the user object
|
||||
*/
|
||||
void user_change_constness (bool constness);
|
||||
|
||||
/**
|
||||
* @brief Assigns the object stored in other to self
|
||||
*
|
||||
|
|
|
|||
|
|
@ -465,6 +465,7 @@ public:
|
|||
virtual bool is_ref () const { return false; }
|
||||
virtual void *deref_proxy (tl::Object *) const { return 0; }
|
||||
virtual const gsi::ClassBase*gsi_cls() const { return 0; }
|
||||
virtual const tl::VariantUserClassBase *change_constness (bool) const { return this; }
|
||||
static BoxClassClass instance;
|
||||
};
|
||||
|
||||
|
|
@ -533,6 +534,7 @@ public:
|
|||
virtual bool is_ref () const { return false; }
|
||||
virtual void *deref_proxy (tl::Object *) const { return 0; }
|
||||
virtual const gsi::ClassBase*gsi_cls() const { return 0; }
|
||||
virtual const tl::VariantUserClassBase *change_constness (bool) const { return this; }
|
||||
static EdgeClassClass instance;
|
||||
};
|
||||
|
||||
|
|
@ -1209,3 +1211,4 @@ TEST(20)
|
|||
v = e.parse ("i2.dtrans.disp.y").execute ();
|
||||
EXPECT_EQ (v.to_string (), std::string ("0.3"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3280,6 +3280,44 @@ class BasicTest(unittest.TestCase):
|
|||
self.assertEqual(bb.d4(1, "a", d=pya.BB.E.E3B, c=2.5), "1,a,2.5,101,nil")
|
||||
self.assertEqual(bb.d4(1, "a", 2.0, pya.BB.E.E3B, 42), "1,a,2,101,42")
|
||||
|
||||
# constness
|
||||
def test_93(self):
|
||||
|
||||
b = pya.B()
|
||||
self.assertEqual(b.is_const_object(), False)
|
||||
|
||||
bc = b._to_const_object()
|
||||
self.assertEqual(bc.is_const_object(), True)
|
||||
|
||||
m = ""
|
||||
try:
|
||||
bc.set_str("abc")
|
||||
self.assertEqual(1, 0)
|
||||
except Exception as ex:
|
||||
m = str(ex)
|
||||
|
||||
self.assertEqual(m, "Cannot call non-const method on a const reference in B.set_str")
|
||||
|
||||
b = pya.B()
|
||||
bc = b
|
||||
self.assertEqual(b._is_const_object(), False)
|
||||
self.assertEqual(bc._is_const_object(), False)
|
||||
bc = bc._to_const_object()
|
||||
b.set_str("abc")
|
||||
self.assertEqual(b._is_const_object(), False)
|
||||
self.assertEqual(bc._is_const_object(), True)
|
||||
self.assertEqual(b.str(), "abc")
|
||||
self.assertEqual(bc.str(), "abc")
|
||||
|
||||
bnc = bc._const_cast()
|
||||
self.assertEqual(b._is_const_object(), False)
|
||||
self.assertEqual(bc._is_const_object(), True)
|
||||
self.assertEqual(bnc._is_const_object(), False)
|
||||
bnc.set_str("xyz")
|
||||
self.assertEqual(b.str(), "xyz")
|
||||
self.assertEqual(bc.str(), "xyz")
|
||||
self.assertEqual(bnc.str(), "xyz")
|
||||
|
||||
# run unit tests
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestSuite()
|
||||
|
|
|
|||
|
|
@ -3233,4 +3233,45 @@ class Basic_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
# constness
|
||||
def test_82
|
||||
|
||||
b = RBA::B::new
|
||||
assert_equal(b.is_const_object, false)
|
||||
|
||||
bc = b._to_const_object
|
||||
assert_equal(bc.is_const_object, true)
|
||||
|
||||
m = ""
|
||||
begin
|
||||
bc.set_str("abc")
|
||||
assert_equal(1, 0)
|
||||
rescue => ex
|
||||
m = ex.to_s
|
||||
end
|
||||
|
||||
assert_equal(m, "Cannot call non-const method on a const reference in B::set_str")
|
||||
|
||||
b = RBA::B::new
|
||||
bc = b
|
||||
assert_equal(b._is_const_object, false)
|
||||
assert_equal(bc._is_const_object, false)
|
||||
b.set_str("abc")
|
||||
bc._to_const_object
|
||||
assert_equal(b._is_const_object, true) # special
|
||||
assert_equal(bc._is_const_object, true)
|
||||
assert_equal(b.str, "abc")
|
||||
assert_equal(bc.str, "abc")
|
||||
|
||||
bnc = bc._const_cast
|
||||
assert_equal(b._is_const_object, false) # special
|
||||
assert_equal(bc._is_const_object, false) # special
|
||||
assert_equal(bnc._is_const_object, false)
|
||||
bnc.set_str("xyz")
|
||||
assert_equal(b.str, "xyz")
|
||||
assert_equal(bc.str, "xyz")
|
||||
assert_equal(bnc.str, "xyz")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue