From b1ddb702b89f8a7f235bc8fed310cee24ed524d0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Nov 2023 19:06:07 +0100 Subject: [PATCH 1/8] Experimental: implicitly calling constructors from arguments passed tuples or lists for objects - this allows using a (x,y) tuple for Vector or Point arguments --- src/pya/pya/pyaCallables.cc | 27 +++++++------ src/pya/pya/pyaMarshal.cc | 76 +++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index e85010e80e..f63f219769 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -200,7 +200,8 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict) tl_assert (cls_decl != 0); - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + bool is_tuple = PyTuple_Check (args); + int argc = args == NULL ? 0 : (is_tuple ? int (PyTuple_Size (args)) : int (PyList_Size (args))); // get number of candidates by argument count const gsi::MethodBase *meth = 0; @@ -277,9 +278,10 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict) int sc = 0; int i = 0; for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); is_valid && i < argc && a != (*m)->end_arguments (); ++a, ++i) { - if (test_arg (*a, PyTuple_GetItem (args, i), false /*strict*/)) { + PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i); + if (test_arg (*a, arg, false /*strict*/)) { ++sc; - } else if (test_arg (*a, PyTuple_GetItem (args, i), true /*loose*/)) { + } else if (test_arg (*a, arg, true /*loose*/)) { // non-scoring match } else { is_valid = false; @@ -326,7 +328,8 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict) // one candidate, but needs checking whether compatibility is given - this avoid having to route NotImplemented over TypeError exceptions later int i = 0; for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - if (! test_arg (*a, PyTuple_GetItem (args, i), true /*loose*/)) { + PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i); + if (! test_arg (*a, arg, true /*loose*/)) { return 0; } } @@ -607,16 +610,17 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P } } -static void +void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap) { + bool is_tuple = PyTuple_Check (args); int i = 0; - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + int argc = args == NULL ? 0 : (is_tuple ? int (PyTuple_Size (args)) : int (PyList_Size (args))); try { for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); + push_arg (*a, arglist, is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i), heap); } } catch (tl::Exception &ex) { @@ -726,7 +730,7 @@ property_getter_adaptor (int mid, PyObject *self, PyObject *args) PYA_TRY - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + int argc = args == NULL ? 0 : (PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args))); if (argc != 0) { throw tl::Exception (tl::to_string (tr ("Property getters must not have an argument"))); } @@ -747,12 +751,12 @@ property_setter_adaptor (int mid, PyObject *self, PyObject *args) PYA_TRY - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + int argc = args == NULL ? 0 : (PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args))); if (argc != 1) { throw tl::Exception (tl::to_string (tr ("Property setter needs exactly one argument"))); } - PyObject *value = PyTuple_GetItem (args, 0); + PyObject *value = PyTuple_Check (args) ? PyTuple_GetItem (args, 0) : PyList_GetItem (args, 0); if (value) { ret = property_setter_impl (mid, self, value); } @@ -777,7 +781,8 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args) p->destroy (); } - const gsi::MethodBase *meth = match_method (mid, self, args, PyTuple_Size (args) > 0 || ! p->cls_decl ()->can_default_create ()); + int argc = PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args)); + const gsi::MethodBase *meth = match_method (mid, self, args, argc > 0 || ! p->cls_decl ()->can_default_create ()); if (meth && meth->smt () == gsi::MethodBase::None) { diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 0d1274d6cf..d6aae3e014 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -32,6 +32,8 @@ namespace pya { +void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap); + // ------------------------------------------------------------------- // Serialization adaptors for strings, variants, vectors and maps @@ -454,6 +456,8 @@ struct writer { void operator() (gsi::SerialArgs *aa, PyObject *arg, const gsi::ArgType &atype, tl::Heap *heap) { + const gsi::ClassBase *acls = atype.cls (); + if (arg == Py_None || arg == NULL) { if (! (atype.is_ptr () || atype.is_cptr ())) { @@ -465,14 +469,50 @@ struct writer } - if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) { + if (PyTuple_Check (arg) || PyList_Check (arg)) { + + // we may implicitly convert a tuple into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the list given. + + int n = PyTuple_Check (arg) ? int (PyTuple_Size (arg)) : int (PyList_Size (arg)); + const gsi::MethodBase *meth = 0; + for (gsi::ClassBase::method_iterator c = acls->begin_constructors (); c != acls->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + meth = *c; + break; + } + } + + if (!meth) { + throw tl::Exception (tl::to_string (tr ("No constructor of %s available that takes %d arguments (implicit call from tuple)")), acls->name (), n); + } + + // implicit call of constructor + gsi::SerialArgs retlist (meth->retsize ()); + gsi::SerialArgs arglist (meth->argsize ()); + + push_args (arglist, meth, arg, *heap); + + meth->call (0, arglist, retlist); + + void *new_obj = retlist.read (*heap); + if (new_obj && (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ())) { + // For pointers or refs, ownership over these objects is not transferred. + // Hence we have to keep them on the heap. + // TODO: what if the called method takes ownership using keep()? + heap->push (new gsi::ObjectHolder (acls, new_obj)); + } + + aa->write (new_obj); + + } else if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) { const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg)); if (! cls_decl) { throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name)); } - if (cls_decl->is_derived_from (atype.cls ())) { + if (cls_decl->is_derived_from (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); @@ -483,14 +523,15 @@ struct writer aa->write (p->obj ()); } - } else if (cls_decl->can_convert_to (atype.cls ())) { + } else if (cls_decl->can_convert_to (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); // We can convert objects for cref and cptr, but ownership over these objects is not transferred. // Hence we have to keep them on the heap. - void *new_obj = atype.cls ()->create_obj_from (p->cls_decl (), p->obj ()); - heap->push (new gsi::ObjectHolder (atype.cls (), new_obj)); + // TODO: what if the called method takes ownership using keep()? + void *new_obj = acls->create_obj_from (p->cls_decl (), p->obj ()); + heap->push (new gsi::ObjectHolder (acls, new_obj)); aa->write (new_obj); } else { @@ -504,7 +545,7 @@ struct writer throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name)); } - if (cls_decl->is_derived_from (atype.cls ())) { + if (cls_decl->is_derived_from (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); @@ -515,7 +556,7 @@ struct writer aa->write (atype.cls ()->clone (p->obj ())); } - } else if (cls_decl->can_convert_to (atype.cls ())) { + } else if (cls_decl->can_convert_to (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); aa->write (atype.cls ()->create_obj_from (cls_decl, p->obj ())); @@ -1141,19 +1182,38 @@ struct test_arg_func { void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) { + const gsi::ClassBase *acls = atype.cls (); + // for const X * or X *, null is an allowed value if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { *ret = true; return; } + if (loose && (PyTuple_Check (arg) || PyList_Check (arg))) { + + // we may implicitly convert a tuple into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the list given. + + int n = PyTuple_Check (arg) ? int (PyTuple_Size (arg)) : int (PyList_Size (arg)); + *ret = false; + for (gsi::ClassBase::method_iterator c = acls->begin_constructors (); c != acls->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + *ret = true; + break; + } + } + return; + + } + const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg)); if (! cls_decl) { *ret = false; return; } - if (! (cls_decl == atype.cls () || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to(atype.cls ()))))) { + if (! (cls_decl == acls || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to (atype.cls ()))))) { *ret = false; return; } From 38d7d346429164d1d3720f38446159dab646fa9d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Nov 2023 20:22:36 +0100 Subject: [PATCH 2/8] [consider merging] Fixed Python Exit Exception - Python test fails were not recognized --- src/pya/pya/pyaUtils.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pya/pya/pyaUtils.cc b/src/pya/pya/pyaUtils.cc index a279bac189..0bded8e92d 100644 --- a/src/pya/pya/pyaUtils.cc +++ b/src/pya/pya/pyaUtils.cc @@ -117,8 +117,9 @@ void check_error () } else if (PyErr_GivenExceptionMatches (exc_type.get (), PyExc_SystemExit)) { int status = 0; - if (exc_value && test_type (exc_value.get (), true)) { - status = python2c (exc_value.get ()); + if (exc_value) { + tl::Variant st = python2c (exc_value.get ()); + status = st.to_int (); } throw tl::ExitException (status); From b2b950041d9df15c318cebf1490f16194d8f86ab Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Nov 2023 21:41:36 +0100 Subject: [PATCH 3/8] [consider merging] Avoids an assertion when using tuples for out vector parameters --- src/gsi/gsi/gsiSerialisation.cc | 10 +++++++++- src/pya/pya/pyaMarshal.cc | 6 ++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/gsi/gsi/gsiSerialisation.cc b/src/gsi/gsi/gsiSerialisation.cc index 97aa8713ee..1744e567c4 100644 --- a/src/gsi/gsi/gsiSerialisation.cc +++ b/src/gsi/gsi/gsiSerialisation.cc @@ -23,6 +23,7 @@ #include "gsi.h" #include "gsiSerialisation.h" +#include "tlLog.h" namespace gsi { @@ -38,7 +39,14 @@ class AdaptorSynchronizer ~AdaptorSynchronizer () { - mp_src->copy_to (mp_target, *mp_heap); + try { + // NOTE: exceptions must not escape destructors as a basic C++ design requirement + mp_src->copy_to (mp_target, *mp_heap); + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } catch (...) { + } + delete mp_src; delete mp_target; mp_src = 0; diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index d6aae3e014..a476c4d320 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -928,8 +928,10 @@ void PythonBasedVectorAdaptor::push (gsi::SerialArgs &r, tl::Heap &heap) void PythonBasedVectorAdaptor::clear () { - if (PySequence_Check (m_array.get ())) { - PySequence_DelSlice (m_array.get (), 0, PySequence_Length (m_array.get ())); + if (PyList_Check (m_array.get ())) { + PyList_SetSlice (m_array.get (), 0, PyList_Size (m_array.get ()), NULL); + } else if (PyTuple_Check (m_array.get ())) { + throw tl::Exception (tl::to_string (tr ("Tuples cannot be modified and cannot be used as out parameters"))); } } From a95706baaad680913bebf138024c56c609e83748 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Nov 2023 21:42:50 +0100 Subject: [PATCH 4/8] Tests for implict construction from tuple or list arguments --- src/gsi/gsi_test/gsiTest.cc | 123 +++++++++++++-------- src/gsi/gsi_test/gsiTest.h | 143 +++++++++++++++--------- testdata/python/basic.py | 215 ++++++++++++++++++++++++------------ 3 files changed, 317 insertions(+), 164 deletions(-) diff --git a/src/gsi/gsi_test/gsiTest.cc b/src/gsi/gsi_test/gsiTest.cc index be3b3921fe..370f055f28 100644 --- a/src/gsi/gsi_test/gsiTest.cc +++ b/src/gsi/gsi_test/gsiTest.cc @@ -65,6 +65,20 @@ A::A (int nn) { f = false; } +A::A (int n1, int n2) { + ++a_count; + e = Enum (0); + n = n1 + n2; + f = false; +} + +A::A (int n1, int n2, double n3) { + ++a_count; + e = Enum (0); + n = (n1 + n2) * n3; + f = false; +} + A::A (const A &a) : gsi::ObjectBase (a) { @@ -339,7 +353,17 @@ static A *a_ctor (int i) return new A (i); } -void A::a20 (A *ptr) +static A *a_ctor2 (int i, int j) +{ + return new A (i, j); +} + +static A *a_ctor3 (int i, int j, double f) +{ + return new A (i, j, f); +} + +void A::a20 (A *ptr) { if (a_inst.get () != ptr) { a_inst.reset (ptr); @@ -360,26 +384,26 @@ static int b_count = 0; B::B () { ++b_count; - av.push_back (A (100)); - av.push_back (A (121)); - av.push_back (A (144)); - avc_nc.push_back (new A_NC (-3100)); - avc_nc.push_back (new A_NC (-3121)); - av_nc.push_back (new A_NC (7100)); - av_nc.push_back (new A_NC (7121)); - av_nc.push_back (new A_NC (7144)); - av_nc.push_back (new A_NC (7169)); + m_av.push_back (A (100)); + m_av.push_back (A (121)); + m_av.push_back (A (144)); + m_avc_nc.push_back (new A_NC (-3100)); + m_avc_nc.push_back (new A_NC (-3121)); + m_av_nc.push_back (new A_NC (7100)); + m_av_nc.push_back (new A_NC (7121)); + m_av_nc.push_back (new A_NC (7144)); + m_av_nc.push_back (new A_NC (7169)); } B::~B () { - while (! av_nc.empty ()) { - delete av_nc.back (); - av_nc.pop_back (); + while (! m_av_nc.empty ()) { + delete m_av_nc.back (); + m_av_nc.pop_back (); } - while (! avc_nc.empty ()) { - delete const_cast (avc_nc.back ()); - avc_nc.pop_back (); + while (! m_avc_nc.empty ()) { + delete const_cast (m_avc_nc.back ()); + m_avc_nc.pop_back (); } if (b_inst == this) { b_inst = 0; @@ -405,22 +429,22 @@ B &B::operator=(const B &d) } m = d.m; - a = d.a; - bv = d.bv; - av = d.av; - while (! av_nc.empty ()) { - delete av_nc.back (); - av_nc.pop_back (); + m_a = d.m_a; + m_bv = d.m_bv; + m_av = d.m_av; + while (! m_av_nc.empty ()) { + delete m_av_nc.back (); + m_av_nc.pop_back (); } - for (std::vector ::const_iterator i = d.av_nc.begin (); i != d.av_nc.end (); ++i) { - av_nc.push_back (new A_NC (**i)); + for (std::vector ::const_iterator i = d.m_av_nc.begin (); i != d.m_av_nc.end (); ++i) { + m_av_nc.push_back (new A_NC (**i)); } - while (! avc_nc.empty ()) { - delete const_cast (avc_nc.back ()); - avc_nc.pop_back (); + while (! m_avc_nc.empty ()) { + delete const_cast (m_avc_nc.back ()); + m_avc_nc.pop_back (); } - for (std::vector ::const_iterator i = d.avc_nc.begin (); i != d.avc_nc.end (); ++i) { - avc_nc.push_back (new A_NC (**i)); + for (std::vector ::const_iterator i = d.m_avc_nc.begin (); i != d.m_avc_nc.end (); ++i) { + m_avc_nc.push_back (new A_NC (**i)); } m_var = d.m_var; m_vars = d.m_vars; @@ -461,7 +485,7 @@ std::string B::addr () const return c; } -static int b3_ext (const B *b, A *aptr) +static int aptr_to_n_ext (const B *b, A *aptr) { return b->b3 (aptr); } @@ -1030,6 +1054,8 @@ static gsi::QFlagsClass decl_qflags_enum ("", "Enums"); static gsi::Class decl_a ("", "A", gsi::constructor ("new_a|new", &a_ctor) + + gsi::constructor ("new", &a_ctor2) + + gsi::constructor ("new", &a_ctor3) + gsi::method ("instance_count", &A::instance_count) + gsi::method ("new_a_by_variant", &A::new_a_by_variant) + @@ -1146,8 +1172,8 @@ static gsi::Class decl_a ("", "A", gsi::method ("af?|af", &A::af1) + gsi::method ("aa", &A::a) + gsi::method ("aa", &A::a_static) + - gsi::method ("a1", &A::a1) + - gsi::method ("a1c", &A::a1c) + + gsi::method ("a1|get_n", &A::a1) + + gsi::method ("a1c|get_n_const", &A::a1c) + gsi::method ("a2", &A::a2) + gsi::method ("a3", &A::a3) + #if defined(HAVE_QT) @@ -1262,8 +1288,8 @@ static gsi::Class decl_b ("", "B", gsi::method ("set_str", &B::set_str) + gsi::method ("str_ccptr", &B::str_ccptr) + gsi::method ("set_str_combine", &B::set_str_combine) + - gsi::method_ext ("b3|aptr_to_n", &b3_ext) + - gsi::method ("b4|aref_to_s", &B::b4) + + gsi::method_ext ("b3|aptr_to_n", &aptr_to_n_ext) + + gsi::method ("b4|aref_to_s", &B::aref_to_s) + gsi::method ("make_a", &B::make_a) + gsi::method ("set_an", &B::set_an) + gsi::method ("an", &B::an) + @@ -1280,18 +1306,23 @@ static gsi::Class decl_b ("", "B", gsi::method ("xxx|amember_cptr", &B::amember_cptr) + gsi::method ("yyy|amember_cref", &B::amember_cref) + gsi::method ("zzz|amember_ref", &B::amember_ref) + - gsi::method ("b15|arg_is_not_nil", &B::b15) + - gsi::method ("b16a|av", &B::b16a) + - gsi::method ("b16b|av_cref", &B::b16b) + - gsi::method ("b16c|av_ref", &B::b16c) + - gsi::method ("b17a|av_cref=", &B::b17a) + - gsi::method ("b17b|av_ref=", &B::b17b) + - gsi::method ("b17c|av=", &B::b17c) + - gsi::method ("b17d|av_cptr=", &B::b17d) + - gsi::method ("b17e|av_ptr=", &B::b17e) + - gsi::iterator ("b18", &B::b18) + - gsi::iterator ("b18b", &B::b18b) + - gsi::iterator ("b18c", &B::b18b) + + gsi::method ("b15|arg_is_not_nil", &B::arg_is_not_nil) + + gsi::method ("b16a|av", &B::av) + + gsi::method ("b16b|av_cref", &B::av_cref) + + gsi::method ("b16c|av_ref", &B::av_ref) + + gsi::method ("push_a", &B::push_a) + + gsi::method ("push_a_cref", &B::push_a_cref) + + gsi::method ("push_a_cptr", &B::push_a_cptr) + + gsi::method ("push_a_ref", &B::push_a_ref) + + gsi::method ("push_a_ptr", &B::push_a_ptr) + + gsi::method ("b17a|av_cref=", &B::set_av_cref) + + gsi::method ("b17b|av_ref=", &B::set_av_ref) + + gsi::method ("b17c|av=", &B::set_av) + + gsi::method ("b17d|av_cptr=", &B::set_av_cptr) + + gsi::method ("b17e|av_ptr=", &B::set_av_ptr) + + gsi::iterator ("b18|each_a", &B::b18) + + gsi::iterator ("b18b|each_a_ref", &B::b18b) + + gsi::iterator ("b18c|each_a_ptr", &B::b18c) + gsi::method ("b20a|var_is_nil", &B::b20a) + gsi::method ("b20b|var_is_double", &B::b20b) + gsi::method ("b20c|var_is_long", &B::b20c) + diff --git a/src/gsi/gsi_test/gsiTest.h b/src/gsi/gsi_test/gsiTest.h index 204d4ae827..fe7f47b540 100644 --- a/src/gsi/gsi_test/gsiTest.h +++ b/src/gsi/gsi_test/gsiTest.h @@ -73,7 +73,17 @@ struct A */ A (int nn); - /** + /** + * @brief Parametrized constructor 2 + */ + A (int n1, int n2); + + /** + * @brief Parametrized constructor 3 + */ + A (int n1, int n2, double n3); + + /** * @brief Copy constructor */ A (const A &a); @@ -640,7 +650,7 @@ struct B return aptr->n; } - std::string b4 (const A &aref) { + std::string aref_to_s (const A &aref) { return tl::sprintf ("b4_result: %d", aref.n); } @@ -650,7 +660,7 @@ struct B void set_an (int n) { - a.n = n; + m_a.n = n; } int an (A a) @@ -660,7 +670,7 @@ struct B void set_an_cref (const int &n) { - a.n = n; + m_a.n = n; } const int &an_cref (const A &a) @@ -670,122 +680,155 @@ struct B std::vector ::const_iterator b10b () const { - return av.begin (); + return m_av.begin (); } std::vector ::const_iterator b10e () const { - return av.end (); + return m_av.end (); } std::vector ::iterator b10b_nc () { - return av.begin (); + return m_av.begin (); } std::vector ::iterator b10e_nc () { - return av.end (); + return m_av.end (); } ValueIter::const_iterator> b11b () const { - return ValueIter::const_iterator> (av.begin ()); + return ValueIter::const_iterator> (m_av.begin ()); } ValueIter::const_iterator> b11e () const { - return ValueIter::const_iterator> (av.end ()); + return ValueIter::const_iterator> (m_av.end ()); } std::vector ::const_iterator b12b () const { - return av_nc.begin (); + return m_av_nc.begin (); } std::vector ::const_iterator b12e () const { - return av_nc.end (); + return m_av_nc.end (); } std::vector ::const_iterator b13b () const { - return avc_nc.begin (); + return m_avc_nc.begin (); } std::vector ::const_iterator b13e () const { - return avc_nc.end (); + return m_avc_nc.end (); } - A *amember_or_nil (bool nn) { return nn ? &a : 0; } - A *amember_ptr () { return &a; } - A &amember_ref () { return a; } - const A *amember_cptr () const { return &a; } - const A &amember_cref () const { return a; } + A *amember_or_nil (bool nn) { return nn ? &m_a : 0; } + A *amember_ptr () { return &m_a; } + A &amember_ref () { return m_a; } + const A *amember_cptr () const { return &m_a; } + const A &amember_cref () const { return m_a; } - bool b15 (A *a) + bool arg_is_not_nil (A *a) { return a != 0; } - std::vector b16a () const + std::vector av () const { - return av; + return m_av; } - const std::vector &b16b () const + const std::vector &av_cref () const { - return av; + return m_av; } - std::vector &b16c () + std::vector &av_ref () { - return av; + return m_av; } - void b17a (const std::vector &v) + void set_av_cref (const std::vector &v) { - av = v; + m_av = v; } - void b17b (std::vector &v) + void set_av_ref (std::vector &v) { - av = v; + m_av = v; } - void b17c (std::vector v) + void set_av (std::vector v) { - av = v; + m_av = v; } - void b17d (const std::vector *v) + void set_av_cptr (const std::vector *v) { if (v) { - av = *v; + m_av = *v; + } else { + m_av.clear (); } } - void b17e (std::vector *v) + void set_av_ptr (std::vector *v) { if (v) { - av = *v; + m_av = *v; + } else { + m_av.clear (); + } + } + + void push_a (A a) + { + m_av.push_back (a); + } + + void push_a_cref (const A &a) + { + m_av.push_back (a); + } + + void push_a_ref (A &a) + { + m_av.push_back (a); + } + + void push_a_cptr (const A *a) + { + if (a) { + m_av.push_back (*a); + } + } + + void push_a_ptr (A *a) + { + if (a) { + m_av.push_back (*a); } } FreeIter::const_iterator> b18 () const { - return FreeIter::const_iterator> (av.begin (), av.end ()); + return FreeIter::const_iterator> (m_av.begin (), m_av.end ()); } FreeIterUseRef::const_iterator> b18b () const { - return FreeIterUseRef::const_iterator> (av.begin (), av.end ()); + return FreeIterUseRef::const_iterator> (m_av.begin (), m_av.end ()); } FreeIterUsePtr::const_iterator> b18c () const { - return FreeIterUsePtr::const_iterator> (av.begin (), av.end ()); + return FreeIterUsePtr::const_iterator> (m_av.begin (), m_av.end ()); } bool b20a (const tl::Variant &var) const { return var.is_nil (); } @@ -862,30 +905,30 @@ struct B FreeIter::const_iterator> each_b_copy () const { - return FreeIter::const_iterator> (bv.begin (), bv.end ()); + return FreeIter::const_iterator> (m_bv.begin (), m_bv.end ()); } FreeIterUseRef::const_iterator> each_b_cref () const { - return FreeIterUseRef::const_iterator> (bv.begin (), bv.end ()); + return FreeIterUseRef::const_iterator> (m_bv.begin (), m_bv.end ()); } FreeIterUsePtr::const_iterator> each_b_cptr () const { - return FreeIterUsePtr::const_iterator> (bv.begin (), bv.end ()); + return FreeIterUsePtr::const_iterator> (m_bv.begin (), m_bv.end ()); } FreeIterUseRef::iterator> each_b_ref () { - return FreeIterUseRef::iterator> (bv.begin (), bv.end ()); + return FreeIterUseRef::iterator> (m_bv.begin (), m_bv.end ()); } FreeIterUsePtr::iterator> each_b_ptr () { - return FreeIterUsePtr::iterator> (bv.begin (), bv.end ()); + return FreeIterUsePtr::iterator> (m_bv.begin (), m_bv.end ()); } - void push_b (const B &b) { bv.push_back (b); } + void push_b (const B &b) { m_bv.push_back (b); } std::map map_iaptr () { return m_map_iaptr; } const std::map &map_iaptr_cref () { return m_map_iaptr; } @@ -996,11 +1039,11 @@ struct B #endif std::string m; - A a; - std::vector bv; - std::vector av; - std::vector av_nc; - std::vector avc_nc; + A m_a; + std::vector m_bv; + std::vector m_av; + std::vector m_av_nc; + std::vector m_avc_nc; tl::Variant m_var; std::vector m_vars; std::map m_map1; diff --git a/testdata/python/basic.py b/testdata/python/basic.py index 3c64efbb79..66e1e730b6 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -35,7 +35,7 @@ def s(self, o): def g(self): return self.offset def m(self): - return self.offset+self.a1() + return self.offset+self.get_n() # Note: there are no protected methods, but this emulates the respective test for RBA def call_a10_prot(self, f): a10_prot(f) @@ -43,7 +43,7 @@ def __repr__(self): return str(self.offset) def repr_of_a(self): - return "a1=" + str(self.a1()) + return "a1=" + str(self.get_n()) pya.A.__repr__ = repr_of_a @@ -150,9 +150,9 @@ def test_00(self): self.assertEqual( pya.A.instance_count(), ic0 + 1 ) a = pya.A() - self.assertEqual(a.a1(), 17) + self.assertEqual(a.get_n(), 17) a.assign(pya.A(110)) - self.assertEqual(a.a1(), 110) + self.assertEqual(a.get_n(), 110) a = None self.assertEqual( pya.A.instance_count(), ic0 ) @@ -182,13 +182,13 @@ def test_00(self): self.assertEqual( pya.A.aa(), "static_a" ) self.assertEqual( a.aa(), "a" ) - self.assertEqual( a.a1(), 17 ) + self.assertEqual( a.get_n(), 17 ) a.a5(-5) - self.assertEqual( a.a1(), -5 ) + self.assertEqual( a.get_n(), -5 ) a.a5(0x7fffffff) - self.assertEqual( a.a1(), 0x7fffffff ) + self.assertEqual( a.get_n(), 0x7fffffff ) a.a5(-0x80000000) - self.assertEqual( a.a1(), -0x80000000 ) + self.assertEqual( a.get_n(), -0x80000000 ) self.assertEqual( a.a3("a"), 1 ) self.assertEqual( a.a3("ab"), 2 ) @@ -249,7 +249,7 @@ def test_00(self): self.assertEqual( pya.A.instance_count(), ic0 ) a = pya.A.new_a( 55 ) self.assertEqual( pya.A.instance_count(), ic0 + 1 ) - self.assertEqual( a.a1(), 55 ) + self.assertEqual( a.get_n(), 55 ) self.assertEqual( a.a_vp1( a.a_vp2() ), "abc" ) a.destroy() self.assertEqual( pya.A.instance_count(), ic0 ) @@ -403,9 +403,9 @@ def test_10(self): a = pya.A.new_a_by_variant() self.assertEqual(pya.A.instance_count(), ic0 + 1) - self.assertEqual(a.a1(), 17) + self.assertEqual(a.get_n(), 17) a.a5(-15) - self.assertEqual(a.a1(), -15) + self.assertEqual(a.get_n(), -15) a = None self.assertEqual(pya.A.instance_count(), ic0) @@ -422,6 +422,85 @@ def test_10(self): b._destroy() self.assertEqual(pya.B.instance_count(), ic0) + def test_11(self): + + # implicitly converting tuples/lists to objects by calling the constructor + + b = pya.B() + b.av_cptr = [ pya.A(17), [1,2], [4,6,0.5] ] + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [17, 3, 5]) + + b = pya.B() + # NOTE: this gives an error (printed only) that tuples can't be modified as out parameters + b.av_ref = ( (1,2), (6,2,0.25), [42] ) + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [3, 2, 42]) + + b = pya.B() + aa = [ (1,2), (6,2,0.25), [42] ] + b.av_ptr = aa + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [3, 2, 42]) + + # NOTE: as we used aa in "av_ptr", it got modified as out parameter and + # now holds A object references + arr = [] + for a in aa: + arr.append(a.get_n_const()) + self.assertEqual(arr, [3, 2, 42]) + + b.av = () + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, []) + + b.push_a_ref( (1, 7) ) + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [8]) + + b.push_a_ptr( (1, 7, 0.25) ) + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [8, 2]) + + b.push_a_cref( [42] ) + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [8, 2, 42]) + + b.push_a_cptr( (1, 16) ) + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [8, 2, 42, 17]) + + b.push_a( (4, 6, 0.5) ) + + arr = [] + for a in b.each_a(): + arr.append(a.get_n_const()) + self.assertEqual(arr, [8, 2, 42, 17, 5]) + def test_12(self): a1 = pya.A() @@ -429,16 +508,16 @@ def test_12(self): a2 = a1 a3 = a2.dup() - self.assertEqual( a1.a1(), -15 ) - self.assertEqual( a2.a1(), -15 ) - self.assertEqual( a3.a1(), -15 ) + self.assertEqual( a1.get_n(), -15 ) + self.assertEqual( a2.get_n(), -15 ) + self.assertEqual( a3.get_n(), -15 ) a1.a5( 11 ) a3.a5( -11 ) - self.assertEqual( a1.a1(), 11 ) - self.assertEqual( a2.a1(), 11 ) - self.assertEqual( a3.a1(), -11 ) + self.assertEqual( a1.get_n(), 11 ) + self.assertEqual( a2.get_n(), 11 ) + self.assertEqual( a3.get_n(), -11 ) self.assertEqual( a1.a10_d(5.2), "5.2" ) self.assertEqual( a1.a10_s(0x70000000), "0" ) @@ -651,7 +730,7 @@ def test_13(self): err_caught = False try: - b.amember_cptr().a1() # cannot call non-const method on const reference + b.amember_cptr().get_n() # cannot call non-const method on const reference except: err_caught = True self.assertEqual( err_caught, True ) @@ -675,7 +754,7 @@ def test_13(self): try: for a in b.b10(): - arr.append(a.a1()) # b10 is a const iterator - cannot call a1 on it + arr.append(a.get_n()) # b10 is a const iterator - cannot call a1 on it except: err_caught = True self.assertEqual( err_caught, True ) @@ -687,7 +766,7 @@ def test_13(self): try: for a in b.b10p(): - arr.append(a.a1()) # b10p is a const iterator - cannot call a1 on it + arr.append(a.get_n()) # b10p is a const iterator - cannot call a1 on it except: err_caught = True self.assertEqual( err_caught, True ) @@ -695,7 +774,7 @@ def test_13(self): arr = [] for a in b.b10(): - arr.append(a.dup().a1()) + arr.append(a.dup().get_n()) self.assertEqual(arr, [100, 121, 144]) arr = [] @@ -706,99 +785,99 @@ def test_13(self): # destroyed too early. bdup = b.dup() for a in bdup.b10(): - arr.append(a.dup().a1()) + arr.append(a.dup().get_n()) self.assertEqual(arr, [100, 121, 144]) return arr = [] for a in b.b10(): - arr.append(a.a1c()) + arr.append(a.get_n_const()) self.assertEqual(arr, [100, 121, 144]) arr = [] for a in b.b10p(): - arr.append(a.dup().a1()) + arr.append(a.dup().get_n()) self.assertEqual(arr, [100, 121, 144]) arr = [] # Ticket #811: for a in b.dup().b10p(): - arr.append(a.dup().a1()) + arr.append(a.dup().get_n()) self.assertEqual(arr, [100, 121, 144]) arr = [] bdup = b.dup() for a in bdup.b10p(): - arr.append(a.dup().a1()) + arr.append(a.dup().get_n()) self.assertEqual(arr, [100, 121, 144]) arr = [] for a in b.b10p(): - arr.append(a.a1c()) + arr.append(a.get_n_const()) self.assertEqual(arr, [100, 121, 144]) arr = [] for a in b.b11(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [100, 121, 144]) arr = [] bdup = b.dup() for a in bdup.b11(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [100, 121, 144]) arr = [] for a in b.b12(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [7100, 7121, 7144, 7169]) arr = [] bdup = b.dup() for a in bdup.b12(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [7100, 7121, 7144, 7169]) aarr = b.b16a() arr = [] for a in aarr: - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [100, 121, 144]) aarr = b.b16b() arr = [] for a in aarr: - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [100, 121, 144]) aarr = b.b16c() arr = [] for a in aarr: - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [100, 121, 144]) b.b17a( [ pya.A.new_a( 101 ), pya.A.new_a( -122 ) ] ) arr = [] for a in b.b11(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [101, -122]) b.b17a( [] ) arr = [] for a in b.b11(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, []) b.b17b( [ pya.A.new_a( 102 ), pya.A.new_a( -123 ) ] ) arr = [] for a in b.b11(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [102, -123]) b.b17c( [ pya.A.new_a( 100 ), pya.A.new_a( 121 ), pya.A.new_a( 144 ) ] ) arr = [] for a in b.b11(): - arr.append(a.a1()) + arr.append(a.get_n()) self.assertEqual(arr, [100, 121, 144]) if not leak_check: @@ -806,7 +885,7 @@ def test_13(self): arr = [] try: for a in b.b13(): - arr.append(a.a1()) + arr.append(a.get_n()) except: err_caught = True self.assertEqual( err_caught, True ) @@ -814,28 +893,28 @@ def test_13(self): arr = [] for a in b.b13(): - arr.append(a.a1c()) + arr.append(a.get_n_const()) self.assertEqual(arr, [-3100, -3121]) arr = [] bdup = b.dup() for a in bdup.b13(): - arr.append(a.a1c()) + arr.append(a.get_n_const()) self.assertEqual(arr, [-3100, -3121]) arr = [] - for a in b.b18(): - arr.append(a.a1c()) + for a in b.each_a(): + arr.append(a.get_n_const()) self.assertEqual(arr, [100, 121, 144]) arr = [] - for a in b.b18(): - arr.append(a.a1()) + for a in b.each_a(): + arr.append(a.get_n()) self.assertEqual(arr, [100, 121, 144]) arr = [] - for a in b.b18b(): - arr.append(a.a1c()) + for a in b.each_a_ref(): + arr.append(a.get_n_const()) self.assertEqual(arr, [100, 121, 144]) arr = [] @@ -843,8 +922,8 @@ def test_13(self): # since A is a managed object and is not turned into a copy. err_caught = False try: - for a in b.b18b(): - arr.append(a.a1()) + for a in b.each_a_ref(): + arr.append(a.get_n()) except: err_caught = True end @@ -852,17 +931,17 @@ def test_13(self): self.assertEqual(err_caught, True) arr = [] - for a in b.b18c(): - arr.append(a.a1c()) + for a in b.each_a_ptr(): + arr.append(a.get_n_const()) self.assertEqual(arr, [100, 121, 144]) arr = [] - # this does not work since b18c delivers a "const *" which cannot be used to call a non-const + # this does not work since each_a_ptr delivers a "const *" which cannot be used to call a non-const # method on err_caught = False try: - for a in b.b18c(): - arr.append(a.a1()) + for a in b.each_a_ptr(): + arr.append(a.get_n()) except: err_caught = True end @@ -1035,23 +1114,23 @@ def test_17(self): a_count = pya.A.instance_count() a = b.make_a( 1971 ); self.assertEqual( pya.A.instance_count(), a_count + 1 ) - self.assertEqual( a.a1(), 1971 ); + self.assertEqual( a.get_n(), 1971 ); self.assertEqual( b.an( a ), 1971 ); aa = b.make_a( -61 ); self.assertEqual( pya.A.instance_count(), a_count + 2 ) self.assertEqual( b.an_cref( aa ), -61 ); - self.assertEqual( a.a1(), 1971 ); + self.assertEqual( a.get_n(), 1971 ); self.assertEqual( b.an( a ), 1971 ); - self.assertEqual( aa.a1(), -61 ); + self.assertEqual( aa.get_n(), -61 ); self.assertEqual( b.an( aa ), -61 ); aa.a5(98); a.a5(100); - self.assertEqual( a.a1(), 100 ); + self.assertEqual( a.get_n(), 100 ); self.assertEqual( b.an( a ), 100 ); - self.assertEqual( aa.a1(), 98 ); + self.assertEqual( aa.get_n(), 98 ); self.assertEqual( b.an( aa ), 98 ); a._destroy() @@ -1064,10 +1143,10 @@ def test_18(self): b = pya.B() b.set_an( 77 ) - self.assertEqual( b.amember_cptr().a1c(), 77 ); + self.assertEqual( b.amember_cptr().get_n_const(), 77 ); b.set_an_cref( 79 ) - self.assertEqual( b.amember_cptr().a1c(), 79 ); + self.assertEqual( b.amember_cptr().get_n_const(), 79 ); aref = b.amember_cptr() err_caught = False @@ -1075,14 +1154,14 @@ def test_18(self): if not leak_check: try: - x = aref.a1() # cannot call non-const method on const reference (as delivered by amember_cptr) + x = aref.get_n() # cannot call non-const method on const reference (as delivered by amember_cptr) except: err_caught = True self.assertEqual( err_caught, True ) - self.assertEqual( aref.a1c(), 79 ); + self.assertEqual( aref.get_n_const(), 79 ); b.set_an( -1 ) - self.assertEqual( aref.a1c(), -1 ); + self.assertEqual( aref.get_n_const(), -1 ); def test_19(self): @@ -1140,11 +1219,11 @@ def test_20(self): a1 = b.amember_or_nil_alt( True ) a2 = b.amember_ptr_alt() - self.assertEqual( a1.a1(), 17 ) - self.assertEqual( a2.a1(), 17 ) + self.assertEqual( a1.get_n(), 17 ) + self.assertEqual( a2.get_n(), 17 ) a1.a5( 761 ) - self.assertEqual( a1.a1(), 761 ) - self.assertEqual( a2.a1(), 761 ) + self.assertEqual( a1.get_n(), 761 ) + self.assertEqual( a2.get_n(), 761 ) a1 = b.amember_or_nil( False ) self.assertEqual( a1, None ) From 2b93dc2dc77e25581ed7ff7a41f50da6dec15b99 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Nov 2023 22:10:56 +0100 Subject: [PATCH 5/8] Implemented implicit constructor from array/tuples also for Ruby --- src/rba/rba/rba.cc | 2 +- src/rba/rba/rbaMarshal.cc | 53 +++++++++ testdata/ruby/basic_testcore.rb | 183 +++++++++++++++++++++----------- 3 files changed, 176 insertions(+), 62 deletions(-) diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 00826361c3..7883c78bcf 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -943,7 +943,7 @@ static gsi::ArgType create_void_type () static gsi::ArgType s_void_type = create_void_type (); -static void +void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, VALUE *argv, int argc, tl::Heap &heap) { int i = 0; diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index 7478ecbc2b..256eef7fd3 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -34,6 +34,8 @@ namespace rba { +void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, VALUE *argv, int argc, tl::Heap &heap); + // ------------------------------------------------------------------- // Serialization adaptors for strings, variants, vectors and maps @@ -497,6 +499,42 @@ struct writer aa->write ((void *) 0); } + } else if (TYPE (arg) == T_ARRAY) { + + // we may implicitly convert an array into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the array given. + + int n = RARRAY_LEN (arg); + const gsi::MethodBase *meth = 0; + for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + meth = *c; + break; + } + } + + if (!meth) { + throw tl::Exception (tl::to_string (tr ("No constructor of %s available that takes %d arguments (implicit call from tuple)")), atype.cls ()->name (), n); + } + + // implicit call of constructor + gsi::SerialArgs retlist (meth->retsize ()); + gsi::SerialArgs arglist (meth->argsize ()); + + push_args (arglist, meth, RARRAY_PTR (arg), n, *heap); + + meth->call (0, arglist, retlist); + + void *new_obj = retlist.read (*heap); + if (new_obj && (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ())) { + // For pointers or refs, ownership over these objects is not transferred. + // Hence we have to keep them on the heap. + // TODO: what if the called method takes ownership using keep()? + heap->push (new gsi::ObjectHolder (atype.cls (), new_obj)); + } + + aa->write (new_obj); + } else { if (TYPE (arg) != T_DATA) { @@ -1152,6 +1190,21 @@ struct test_arg_func // for const X * or X *, nil is an allowed value *ret = true; + } else if (loose && TYPE (arg) == T_ARRAY) { + + // we may implicitly convert an array into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the array given. + + int n = RARRAY_LEN (arg); + + *ret = false; + for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + *ret = true; + break; + } + } + } else { *ret = (TYPE (arg) == T_DATA); diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 87f2f1e4be..5876e1bda1 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -21,9 +21,9 @@ def test_FIRST assert_equal( RBA::A::instance_count, ic0 + 1 ) a = RBA::A.new - assert_equal(a.a1, 17) + assert_equal(a.get_n, 17) a.assign(RBA::A.new(110)) - assert_equal(a.a1, 110) + assert_equal(a.get_n, 110) a = nil GC.start @@ -76,13 +76,13 @@ def test_FIRST assert_equal( RBA::A.aa, "static_a" ) assert_equal( a.aa, "a" ) - assert_equal( a.a1, 17 ) + assert_equal( a.get_n, 17 ) a.a5 -5 - assert_equal( a.a1, -5 ) + assert_equal( a.get_n, -5 ) a.a5 0x7fffffff - assert_equal( a.a1, 0x7fffffff ) + assert_equal( a.get_n, 0x7fffffff ) a.a5 -0x80000000 - assert_equal( a.a1, -0x80000000 ) + assert_equal( a.get_n, -0x80000000 ) assert_equal( a.a3("a"), 1 ) assert_equal( a.a3("ab"), 2 ) @@ -142,7 +142,7 @@ def test_FIRST assert_equal( RBA::A::instance_count, ic0 ) a = RBA::A::new_a( 55 ) assert_equal( RBA::A::instance_count, ic0 + 1 ) - assert_equal( a.a1, 55 ) + assert_equal( a.get_n, 55 ) assert_equal( a.a_vp1( a.a_vp2 ), "abc" ) a.destroy assert_equal( RBA::A::instance_count, ic0 ) @@ -302,9 +302,9 @@ def test_10 a = RBA::A.new_a_by_variant assert_equal(RBA::A::instance_count, ic0 + 1) - assert_equal(a.a1, 17) + assert_equal(a.get_n, 17) a.a5(-15) - assert_equal(a.a1, -15) + assert_equal(a.get_n, -15) a = nil GC.start @@ -327,6 +327,67 @@ def test_10 end + def test_11 + + # implicitly converting tuples/lists to objects by calling the constructor + + b = RBA::B::new() + b.av_cptr = [ RBA::A::new(17), [1,2], [4,6,0.5] ] + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [17, 3, 5]) + + b = RBA::B::new() + # NOTE: this gives an error (printed only) that tuples can't be modified as out parameters + b.av_ref = [ [1,2], [6,2,0.25], [42] ] + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [3, 2, 42]) + + b = RBA::B::new() + aa = [ [1,2], [6,2,0.25], [42] ] + b.av_ptr = aa + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [3, 2, 42]) + + # NOTE: as we used aa in "av_ptr", it got modified as out parameter and + # now holds A object references + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [3, 2, 42]) + + b.av = [] + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, []) + + b.push_a_ref([1, 7]) + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [8]) + + b.push_a_ptr([1, 7, 0.25]) + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [8, 2]) + + b.push_a_cref([42]) + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [8, 2, 42]) + + b.push_a_cptr([1, 16]) + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [8, 2, 42, 17]) + + b.push_a([4, 6, 0.5]) + + arr = b.each_a.collect { |a| a.get_n_const } + assert_equal(arr, [8, 2, 42, 17, 5]) + + end + def test_12 a1 = RBA::A.new @@ -334,16 +395,16 @@ def test_12 a2 = a1 a3 = a2.dup - assert_equal( a1.a1, -15 ) - assert_equal( a2.a1, -15 ) - assert_equal( a3.a1, -15 ) + assert_equal( a1.get_n, -15 ) + assert_equal( a2.get_n, -15 ) + assert_equal( a3.get_n, -15 ) a1.a5( 11 ) a3.a5( -11 ) - assert_equal( a1.a1, 11 ) - assert_equal( a2.a1, 11 ) - assert_equal( a3.a1, -11 ) + assert_equal( a1.get_n, 11 ) + assert_equal( a2.get_n, 11 ) + assert_equal( a3.get_n, -11 ) assert_equal( a1.a10_s(0x70000000), "0" ) assert_equal( a1.a10_s(0x7fffffff), "-1" ) @@ -564,7 +625,7 @@ def test_13 err_caught = false begin - b.amember_cptr.a1 # cannot call non-const method on const reference + b.amember_cptr.get_n # cannot call non-const method on const reference rescue err_caught = true end @@ -590,7 +651,7 @@ def test_13 if !$leak_check begin - b.b10 { |a| arr.push(a.a1) } # b10 is a const iterator - cannot call a1 on it + b.b10 { |a| arr.push(a.get_n) } # b10 is a const iterator - cannot call a1 on it rescue err_caught = true end @@ -604,7 +665,7 @@ def test_13 if !$leak_check begin - b.b10p { |a| arr.push(a.a1) } # b10p is a const iterator - cannot call a1 on it + b.b10p { |a| arr.push(a.get_n) } # b10p is a const iterator - cannot call a1 on it rescue err_caught = true end @@ -614,85 +675,85 @@ def test_13 end arr = [] - b.b10 { |a| arr.push(a.dup.a1) } + b.b10 { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.dup.b10 { |a| arr.push(a.dup.a1) } + b.dup.b10 { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b10 { |a| arr.push(a.a1c) } + b.b10 { |a| arr.push(a.get_n_const) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b10p { |a| arr.push(a.dup.a1) } + b.b10p { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.dup.b10p { |a| arr.push(a.dup.a1) } + b.dup.b10p { |a| arr.push(a.dup.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b10p { |a| arr.push(a.a1c) } + b.b10p { |a| arr.push(a.get_n_const) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b11 { |a| arr.push(a.a1) } + b.b11 { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.dup.b11 { |a| arr.push(a.a1) } + b.dup.b11 { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b12 { |a| arr.push(a.a1) } + b.b12 { |a| arr.push(a.get_n) } assert_equal(arr, [7100, 7121, 7144, 7169]) arr = [] - b.dup.b12 { |a| arr.push(a.a1) } + b.dup.b12 { |a| arr.push(a.get_n) } assert_equal(arr, [7100, 7121, 7144, 7169]) aarr = b.b16a arr = [] - aarr.each { |a| arr.push(a.a1) } + aarr.each { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) aarr = b.b16b arr = [] - aarr.each { |a| arr.push(a.a1) } + aarr.each { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) aarr = b.b16c arr = [] - aarr.each { |a| arr.push(a.a1) } + aarr.each { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) b.b17a( [ RBA::A.new_a( 101 ), RBA::A.new_a( -122 ) ] ) arr = [] - b.b11 { |a| arr.push(a.a1) } + b.b11 { |a| arr.push(a.get_n) } assert_equal(arr, [101, -122]) b.b17a( [] ) arr = [] - b.b11 { |a| arr.push(a.a1) } + b.b11 { |a| arr.push(a.get_n) } assert_equal(arr, []) b.b17b( [ RBA::A.new_a( 102 ), RBA::A.new_a( -123 ) ] ) arr = [] - b.b11 { |a| arr.push(a.a1) } + b.b11 { |a| arr.push(a.get_n) } assert_equal(arr, [102, -123]) b.b17c( [ RBA::A.new_a( 100 ), RBA::A.new_a( 121 ), RBA::A.new_a( 144 ) ] ) arr = [] - b.b11 { |a| arr.push(a.a1) } + b.b11 { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) if !$leak_check arr = [] begin - b.b13 { |a| arr.push(a.a1) } + b.b13 { |a| arr.push(a.get_n) } rescue err_caught = true end @@ -702,31 +763,31 @@ def test_13 end arr = [] - b.b13 { |a| arr.push(a.a1c) } + b.b13 { |a| arr.push(a.get_n_const) } assert_equal(arr, [-3100, -3121]) arr = [] - b.dup.b13 { |a| arr.push(a.a1c) } + b.dup.b13 { |a| arr.push(a.get_n_const) } assert_equal(arr, [-3100, -3121]) arr = [] - b.b18 { |a| arr.push(a.a1c) } + b.each_a { |a| arr.push(a.get_n_const) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b18 { |a| arr.push(a.a1) } + b.each_a { |a| arr.push(a.get_n) } assert_equal(arr, [100, 121, 144]) arr = [] - b.b18b { |a| arr.push(a.a1c) } + b.each_a_ref { |a| arr.push(a.get_n_const) } assert_equal(arr, [100, 121, 144]) arr = [] - # even though b18b returns a "const A &", calling a non-const method does not work + # even though each_a_ref returns a "const A &", calling a non-const method does not work # since A is a managed object and is not turned into a copy. err_caught = false begin - b.b18b { |a| arr.push(a.a1) } + b.each_a_ref { |a| arr.push(a.get_n) } rescue err_caught = true end @@ -734,15 +795,15 @@ def test_13 assert_equal(err_caught, true) arr = [] - b.b18c { |a| arr.push(a.a1c) } + b.each_a_ptr { |a| arr.push(a.get_n_const) } assert_equal(arr, [100, 121, 144]) arr = [] - # this does not work since b18c delivers a "const *" which cannot be used to call a non-const + # this does not work since each_a_ptr delivers a "const *" which cannot be used to call a non-const # method on err_caught = false begin - b.b18c { |a| arr.push(a.a1) } + b.each_a_ptr { |a| arr.push(a.get_n) } rescue err_caught = true end @@ -952,23 +1013,23 @@ def test_17 a = b.make_a( 1971 ); assert_equal( RBA::A.instance_count, a_count + 1 ) - assert_equal( a.a1, 1971 ); + assert_equal( a.get_n, 1971 ); assert_equal( b.an( a ), 1971 ); aa = b.make_a( -61 ); assert_equal( RBA::A.instance_count, a_count + 2 ) assert_equal( b.an_cref( aa ), -61 ); - assert_equal( a.a1, 1971 ); + assert_equal( a.get_n, 1971 ); assert_equal( b.an( a ), 1971 ); - assert_equal( aa.a1, -61 ); + assert_equal( aa.get_n, -61 ); assert_equal( b.an( aa ), -61 ); aa.a5 98; a.a5 100; - assert_equal( a.a1, 100 ); + assert_equal( a.get_n, 100 ); assert_equal( b.an( a ), 100 ); - assert_equal( aa.a1, 98 ); + assert_equal( aa.get_n, 98 ); assert_equal( b.an( aa ), 98 ); a._destroy @@ -984,10 +1045,10 @@ def test_18 b = RBA::B.new b.set_an( 77 ) - assert_equal( b.amember_cptr.a1c, 77 ); + assert_equal( b.amember_cptr.get_n_const, 77 ); b.set_an_cref( 79 ) - assert_equal( b.amember_cptr.a1c, 79 ); + assert_equal( b.amember_cptr.get_n_const, 79 ); aref = b.amember_cptr err_caught = false @@ -995,17 +1056,17 @@ def test_18 if !$leak_check begin - x = aref.a1 # cannot call non-const method on const reference (as delivered by amember_cptr) + x = aref.get_n # cannot call non-const method on const reference (as delivered by amember_cptr) rescue err_caught = true end assert_equal( err_caught, true ) - assert_equal( aref.a1c, 79 ); + assert_equal( aref.get_n_const, 79 ); end b.set_an( -1 ) - assert_equal( aref.a1c, -1 ); + assert_equal( aref.get_n_const, -1 ); end @@ -1095,11 +1156,11 @@ def test_20 a1 = b.amember_or_nil( true ) a2 = b.amember_ptr - assert_equal( a1.a1, 17 ) - assert_equal( a2.a1, 17 ) + assert_equal( a1.get_n, 17 ) + assert_equal( a2.get_n, 17 ) a1.a5( 761 ) - assert_equal( a1.a1, 761 ) - assert_equal( a2.a1, 761 ) + assert_equal( a1.get_n, 761 ) + assert_equal( a2.get_n, 761 ) a1 = b.amember_or_nil( false ) assert_equal( a1, nil ) @@ -2985,7 +3046,7 @@ def test_optional assert_equal(RBA::B::int_to_optional(1, true), 1) assert_equal(RBA::B::int_to_optional(1, false), nil) - assert_equal(RBA::B::int_to_optional_a(1, true).a1, 1) + assert_equal(RBA::B::int_to_optional_a(1, true).get_n, 1) assert_equal(RBA::B::int_to_optional_a(1, false), nil) assert_equal(RBA::B::optional_to_int(1, -1), 1) From cb1589b2ba7176a0da81274e0d95485364ea8389 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Nov 2023 16:19:09 +0100 Subject: [PATCH 6/8] Ported upgrade to expressions too --- src/gsi/gsi/gsiExpression.cc | 129 ++++++++++++++++------- src/gsi/unit_tests/gsiExpressionTests.cc | 55 +++++++--- src/tl/tl/tlUnitTest.cc | 10 +- 3 files changed, 141 insertions(+), 53 deletions(-) diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 9a979e9e7c..48f7fb39b8 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -286,6 +286,25 @@ struct test_arg_func return; } + if (arg.is_list ()) { + + // we may implicitly convert an array into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the array given. + + int n = arg.size (); + + *ret = false; + for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + *ret = true; + break; + } + } + + return; + + } + if (! arg.is_user ()) { *ret = false; return; @@ -628,29 +647,77 @@ struct writer { void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap) { - if (atype.is_ref () || atype.is_cref () || atype.is_ptr () || atype.is_cptr ()) { + if (arg->is_nil ()) { - if (arg->is_nil ()) { + if (atype.is_ref () || atype.is_cref ()) { + throw tl::Exception (tl::to_string (tr ("Cannot pass nil to reference parameters"))); + } else if (! atype.is_cptr () && ! atype.is_ptr ()) { + throw tl::Exception (tl::to_string (tr ("Cannot pass nil to direct parameters"))); + } - if (atype.is_ref () || atype.is_cref ()) { - throw tl::Exception (tl::to_string (tr ("Cannot pass nil to reference parameters"))); - } + aa->write ((void *) 0); - aa->write ((void *) 0); + } else if (arg->is_list ()) { - } else { + // we may implicitly convert an array into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the array given. - if (! arg->is_user ()) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); + int n = arg->size (); + const gsi::MethodBase *meth = 0; + for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + meth = *c; + break; } + } - const tl::VariantUserClassBase *cls = arg->user_cls (); - if (!cls) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); - } - if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ())); + if (!meth) { + throw tl::Exception (tl::to_string (tr ("No constructor of %s available that takes %d arguments (implicit call from tuple)")), atype.cls ()->name (), n); + } + + // implicit call of constructor + gsi::SerialArgs retlist (meth->retsize ()); + gsi::SerialArgs arglist (meth->argsize ()); + + int narg = 0; + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && narg < n; ++a, ++narg) { + try { + // Note: this const_cast is ugly, but it will basically enable "out" parameters + // TODO: clean this up. + gsi::do_on_type () (a->type (), &arglist, (arg->get_list ().begin () + narg).operator-> (), *a, heap); + } catch (tl::Exception &ex) { + std::string msg = ex.msg () + tl::sprintf (tl::to_string (tr (" (argument '%s')")), a->spec ()->name ()); + throw tl::Exception (msg); } + } + + meth->call (0, arglist, retlist); + + void *new_obj = retlist.read (*heap); + if (new_obj && (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ())) { + // For pointers or refs, ownership over these objects is not transferred. + // Hence we have to keep them on the heap. + // TODO: what if the called method takes ownership using keep()? + heap->push (new gsi::ObjectHolder (atype.cls (), new_obj)); + } + + aa->write (new_obj); + + } else { + + if (! arg->is_user ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); + } + + const tl::VariantUserClassBase *cls = arg->user_cls (); + if (!cls) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); + } + if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ())); + } + + if (atype.is_ref () || atype.is_cref () || atype.is_ptr () || atype.is_cptr ()) { if (cls->gsi_cls ()->is_derived_from (atype.cls ())) { @@ -673,36 +740,24 @@ struct writer throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); } - } + } else { - } else { + if (cls->gsi_cls ()->is_derived_from (atype.cls ())) { - if (! arg->is_user ()) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); - } + if (cls->gsi_cls ()->adapted_type_info ()) { + aa->write (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg))); + } else { + aa->write ((void *) cls->gsi_cls ()->clone (get_object (*arg))); + } - const tl::VariantUserClassBase *cls = arg->user_cls (); - if (!cls) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); - } - if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ())); - } + } else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) { - if (cls->gsi_cls ()->is_derived_from (atype.cls ())) { + aa->write (atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg))); - if (cls->gsi_cls ()->adapted_type_info ()) { - aa->write (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg))); } else { - aa->write ((void *) cls->gsi_cls ()->clone (get_object (*arg))); + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); } - } else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) { - - aa->write (atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg))); - - } else { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); } } diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index a31717fcf4..1bfdf1013b 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -57,14 +57,14 @@ TEST(1) v = e.parse ("A.aa").execute (); EXPECT_EQ (v.to_string (), std::string ("static_a")); - v = e.parse ("A.new.a1").execute (); + v = e.parse ("A.new.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("17")); v = e.parse ("var a=A.new").execute (); - v = e.parse ("a.a5(-5); a.a1").execute (); + v = e.parse ("a.a5(-5); a.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("-5")); // mapping of property assignment to method - v = e.parse ("a.n = -177; a.a1").execute (); + v = e.parse ("a.n = -177; a.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("-177")); bool error = false; try { @@ -104,9 +104,9 @@ TEST(1) v = e.parse ("A.instance_count").execute (); EXPECT_EQ (v.to_int (), base_insts); // remaining instances - v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a1.a1").execute (); + v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a1.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("-15")); - v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a2.a1").execute (); + v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a2.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("107")); v = e.parse ("var a=A.new; a.get_e.to_s").execute (); @@ -218,12 +218,22 @@ TEST(2) EXPECT_EQ (v.to_string (), std::string ("5")); v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = [ a1, a2 ]; to_s(b.av)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); + v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a(a1); b.push_a(a2); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); + v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a_cref(a1); b.push_a_cptr(a2); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); + v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a_ref(a1); b.push_a_ptr(a2); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(1); b.av_cref = [ a1, a2 ]; to_s(b.av_cref)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 1")); - v = e.parse ("var b=B.new; b.av = [ A.new(-13) ]; b.av_cptr = nil; to_s(b.av)").execute (); + v = e.parse ("var b=B.new; b.av_cptr = [ A.new(-13) ]; to_s(b.av)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: -13")); - v = e.parse ("var b=B.new; b.av = [ A.new(13) ]; b.av_ptr = nil; to_s(b.av)").execute (); + v = e.parse ("var b=B.new; b.av_ptr = [ A.new(13) ]; to_s(b.av)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: 13")); + v = e.parse ("var b=B.new; b.av = [ A.new(-13) ]; b.av_cptr = nil; to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("")); + v = e.parse ("var b=B.new; b.av = [ A.new(13) ]; b.av_ptr = nil; to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("")); v = e.parse ("var b=B.new; var a1=A.new(17); b.av_ref = [ a1 ]; to_s(b.av_ref)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: 17")); v = e.parse ("var b=B.new; b.arg_is_not_nil(nil)").execute (); @@ -234,6 +244,21 @@ TEST(2) EXPECT_EQ (v.to_string (), std::string ("17")); v = e.parse ("var b=B.new; b.bx(-1)").execute (); EXPECT_EQ (v.to_string (), std::string ("xz")); + + // List to constructor call + v = e.parse ("var b=B.new; b.av = [ [5, 6], [4, 6, 0.5], [42] ]; to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 11,A: 5,A: 42")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_cref([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_cptr([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_ref([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_ptr([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + /* TODO: No detailed type analysis for ambiguity resolution so far: v = e.parse ("var b=B.new; b.bx('hello', 1)").execute (); @@ -316,15 +341,15 @@ TEST(2) EXPECT_EQ (v.to_string (), std::string ("A: 177")); v = e.parse ("b.amember_or_nil(false)").execute (); EXPECT_EQ (v.to_string (), std::string ("nil")); - v = e.parse ("b.amember_ptr.a5(177); b.amember_ref.a1").execute (); + v = e.parse ("b.amember_ptr.a5(177); b.amember_ref.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("177")); - v = e.parse ("b.amember_ref.a1c").execute (); + v = e.parse ("b.amember_ref.get_n_const").execute (); EXPECT_EQ (v.to_string (), std::string ("177")); - v = e.parse ("b.amember_cref.a1c").execute (); + v = e.parse ("b.amember_cref.get_n_const").execute (); EXPECT_EQ (v.to_string (), std::string ("177")); error = false; try { - v = e.parse ("b.amember_cref.a1").execute (); + v = e.parse ("b.amember_cref.get_n").execute (); } catch (...) { // can't call non-const method on const ref error = true; @@ -334,16 +359,16 @@ TEST(2) // references: storage in variables v = e.parse ("var aref = b.amember_ptr").execute (); v = e.parse ("aref.n = 178").execute (); - v = e.parse ("aref.a1").execute (); + v = e.parse ("aref.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("178")); - v = e.parse ("aref.a1 == 178").execute (); + v = e.parse ("aref.get_n == 178").execute (); EXPECT_EQ (v.to_string (), std::string ("true")); - v = e.parse ("b.amember_ref.a1").execute (); + v = e.parse ("b.amember_ref.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("178")); // references: storage in variables v = e.parse ("var aref = b.amember_cptr").execute (); - v = e.parse ("aref.a1c").execute (); + v = e.parse ("aref.get_n_const").execute (); EXPECT_EQ (v.to_string (), std::string ("178")); error = false; try { diff --git a/src/tl/tl/tlUnitTest.cc b/src/tl/tl/tlUnitTest.cc index 473bff366b..77c1e746a8 100644 --- a/src/tl/tl/tlUnitTest.cc +++ b/src/tl/tl/tlUnitTest.cc @@ -265,7 +265,15 @@ bool TestBase::do_test (bool editable, bool slow) reset_checkpoint (); - execute (this); + try { + execute (this); + } catch (tl::Exception &ex) { + raise (std::string ("Exception caught: ") + ex.msg ()); + } catch (std::runtime_error &ex) { + raise (std::string ("std::runtime_error caught: ") + ex.what ()); + } catch (...) { + raise (std::string ("unspecific exception caught: ")); + } m_testtmp.clear (); From b7d1d22fb21420ead3b6f49fbcff788a5e15f07b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Nov 2023 17:04:44 +0100 Subject: [PATCH 7/8] Fixed Windows builds, enhanced unit test framework with error messages upon exceptions. --- src/gsi/gsi/gsiExpression.cc | 2 +- src/tl/tl/tlUnitTest.cc | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 48f7fb39b8..fb69da4864 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -684,7 +684,7 @@ struct writer try { // Note: this const_cast is ugly, but it will basically enable "out" parameters // TODO: clean this up. - gsi::do_on_type () (a->type (), &arglist, (arg->get_list ().begin () + narg).operator-> (), *a, heap); + gsi::do_on_type () (a->type (), &arglist, const_cast ((arg->get_list ().begin () + narg).operator-> ()), *a, heap); } catch (tl::Exception &ex) { std::string msg = ex.msg () + tl::sprintf (tl::to_string (tr (" (argument '%s')")), a->spec ()->name ()); throw tl::Exception (msg); diff --git a/src/tl/tl/tlUnitTest.cc b/src/tl/tl/tlUnitTest.cc index 77c1e746a8..f6f1ef3d3b 100644 --- a/src/tl/tl/tlUnitTest.cc +++ b/src/tl/tl/tlUnitTest.cc @@ -267,6 +267,8 @@ bool TestBase::do_test (bool editable, bool slow) try { execute (this); + } catch (tl::CancelException &) { + throw; } catch (tl::Exception &ex) { raise (std::string ("Exception caught: ") + ex.msg ()); } catch (std::runtime_error &ex) { From 5a0b7e9fb54bca84a972cd7243ea59be3310bb88 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 26 Nov 2023 18:51:06 +0100 Subject: [PATCH 8/8] Trying to fix MSVC builds --- src/gsi/gsi/gsiExpression.cc | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index fb69da4864..11045a6dfe 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -291,7 +291,7 @@ struct test_arg_func // we may implicitly convert an array into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the array given. - int n = arg.size (); + int n = int (arg.size ()); *ret = false; for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { @@ -639,6 +639,8 @@ struct writer } }; +void push_args (gsi::SerialArgs &arglist, const tl::Variant &args, const gsi::MethodBase *meth, tl::Heap *heap); + /** * @brief Specialization for void */ @@ -662,7 +664,7 @@ struct writer // we may implicitly convert an array into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the array given. - int n = arg->size (); + int n = int (arg->size ()); const gsi::MethodBase *meth = 0; for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { if ((*c)->compatible_with_num_args (n)) { @@ -679,17 +681,7 @@ struct writer gsi::SerialArgs retlist (meth->retsize ()); gsi::SerialArgs arglist (meth->argsize ()); - int narg = 0; - for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && narg < n; ++a, ++narg) { - try { - // Note: this const_cast is ugly, but it will basically enable "out" parameters - // TODO: clean this up. - gsi::do_on_type () (a->type (), &arglist, const_cast ((arg->get_list ().begin () + narg).operator-> ()), *a, heap); - } catch (tl::Exception &ex) { - std::string msg = ex.msg () + tl::sprintf (tl::to_string (tr (" (argument '%s')")), a->spec ()->name ()); - throw tl::Exception (msg); - } - } + push_args (arglist, *arg, meth, heap); meth->call (0, arglist, retlist); @@ -765,6 +757,23 @@ struct writer } }; +void push_args (gsi::SerialArgs &arglist, const tl::Variant &args, const gsi::MethodBase *meth, tl::Heap *heap) +{ + int n = int (args.size ()); + int narg = 0; + + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && narg < n; ++a, ++narg) { + try { + // Note: this const_cast is ugly, but it will basically enable "out" parameters + // TODO: clean this up. + gsi::do_on_type () (a->type (), &arglist, const_cast ((args.get_list ().begin () + narg).operator-> ()), *a, heap); + } catch (tl::Exception &ex) { + std::string msg = ex.msg () + tl::sprintf (tl::to_string (tr (" (argument '%s')")), a->spec ()->name ()); + throw tl::Exception (msg); + } + } +} + // --------------------------------------------------------------------- // Reader function for serialization