WIP: keyword arguments (for now: Python) + transformation alignment

pya.CplxTrans and pya.Trans are good classes for testing the
ability to resolve arguments through keyword parameters.

Keyword parameters are introduced to substitute positional
arguments.
This commit is contained in:
Matthias Koefferlein 2023-12-26 18:56:04 +01:00
parent b128ffc70b
commit 702bcbe924
16 changed files with 1597 additions and 990 deletions

View File

@ -159,7 +159,7 @@ struct trans_defs
"@param c The original transformation\n"
"@param u The Additional displacement\n"
) +
constructor ("new", &new_cxy, arg ("c"), arg ("x"), arg ("y"),
constructor ("new", &new_cxy, arg ("c"), arg ("x", 0), arg ("y", 0),
"@brief Creates a transformation from another transformation plus a displacement\n"
"\n"
"Creates a new transformation from a existing transformation. This constructor is provided for creating duplicates "
@ -172,7 +172,7 @@ struct trans_defs
"@param x The Additional displacement (x)\n"
"@param y The Additional displacement (y)\n"
) +
constructor ("new", &new_rmu, arg ("rot"), arg ("mirr", false), arg ("u", displacement_type ()),
constructor ("new", &new_rmu, arg ("rot", 0), arg ("mirrx", false), arg ("u", displacement_type ()),
"@brief Creates a transformation using angle and mirror flag\n"
"\n"
"The sequence of operations is: mirroring at x axis,\n"
@ -182,7 +182,7 @@ struct trans_defs
"@param mirrx True, if mirrored at x axis\n"
"@param u The displacement\n"
) +
constructor ("new", &new_rmxy, arg ("rot"), arg ("mirr"), arg ("x"), arg ("y"),
constructor ("new", &new_rmxy, arg ("rot", 0), arg ("mirrx", false), arg ("x", 0), arg ("y", 0),
"@brief Creates a transformation using angle and mirror flag and two coordinate values for displacement\n"
"\n"
"The sequence of operations is: mirroring at x axis,\n"
@ -569,7 +569,7 @@ struct cplx_trans_defs
return new C (C (u) * C (mag) * c);
}
static C *new_cmxy (const C &c, double mag, coord_type x, coord_type y)
static C *new_cmxy (const C &c, double mag, target_coord_type x, target_coord_type y)
{
return new C (C (displacement_type (x, y)) * C (mag) * c);
}
@ -584,29 +584,24 @@ struct cplx_trans_defs
return new C (u);
}
static C *new_t (const simple_trans_type &t)
static C *new_tm (const simple_trans_type &t, double mag)
{
return new C (t, 1.0, 1.0);
return new C (t, 1.0, mag);
}
static C *new_tm (const simple_trans_type &t, double m)
static C *new_m (double mag)
{
return new C (t, 1.0, m);
return new C (mag);
}
static C *new_m (double m)
static C *new_mrmu (double mag, double r, bool mirrx, const displacement_type &u)
{
return new C (m);
return new C (mag, r, mirrx, u);
}
static C *new_mrmu (double mag, double r, bool m, const displacement_type &u)
static C *new_mrmxy (double mag, double r, bool mirrx, target_coord_type x, target_coord_type y)
{
return new C (mag, r, m, u);
}
static C *new_mrmxy (double mag, double r, bool m, target_coord_type x, target_coord_type y)
{
return new C (mag, r, m, displacement_type (x, y));
return new C (mag, r, mirrx, displacement_type (x, y));
}
static simple_trans_type s_trans (const C *cplx_trans)
@ -650,7 +645,7 @@ struct cplx_trans_defs
constructor ("new", &new_v,
"@brief Creates a unit transformation\n"
) +
constructor ("new", &new_cmu, arg ("c"), arg ("m", 1.0), arg ("u", displacement_type ()),
constructor ("new", &new_cmu, arg ("c"), arg ("mag", 1.0), arg ("u", displacement_type ()),
"@brief Creates a transformation from another transformation plus a magnification and displacement\n"
"\n"
"Creates a new transformation from a existing transformation. This constructor is provided for creating duplicates "
@ -662,7 +657,7 @@ struct cplx_trans_defs
"@param c The original transformation\n"
"@param u The Additional displacement\n"
) +
constructor ("new", &new_cmxy, arg ("c"), arg ("m"), arg ("x"), arg ("y"),
constructor ("new", &new_cmxy, arg ("c"), arg ("mag", 1.0), arg ("x", 0), arg ("y", 0),
"@brief Creates a transformation from another transformation plus a magnification and displacement\n"
"\n"
"Creates a new transformation from a existing transformation. This constructor is provided for creating duplicates "
@ -684,21 +679,11 @@ struct cplx_trans_defs
"@param x The x displacement\n"
"@param y The y displacement\n"
) +
constructor ("new", &new_m, arg ("m"),
"@brief Creates a transformation from a magnification\n"
"\n"
"Creates a magnifying transformation without displacement and rotation given the magnification m."
) +
constructor ("new", &new_tm, arg ("t"), arg ("m"),
constructor ("new", &new_tm, arg ("t"), arg ("mag", 1.0),
"@brief Creates a transformation from a simple transformation and a magnification\n"
"\n"
"Creates a magnifying transformation from a simple transformation and a magnification."
) +
constructor ("new", &new_t, arg ("t"),
"@brief Creates a transformation from a simple transformation alone\n"
"\n"
"Creates a magnifying transformation from a simple transformation and a magnification of 1.0."
) +
constructor ("new", &new_u, arg ("u"),
"@brief Creates a transformation from a displacement\n"
"\n"
@ -706,7 +691,7 @@ struct cplx_trans_defs
"\n"
"This method has been added in version 0.25."
) +
constructor ("new", &new_mrmu, arg ("mag"), arg ("rot"), arg ("mirrx"), arg ("u"),
constructor ("new", &new_mrmu, arg ("mag", 1.0), arg ("rot", 0.0), arg ("mirrx", false), arg ("u", displacement_type ()),
"@brief Creates a transformation using magnification, angle, mirror flag and displacement\n"
"\n"
"The sequence of operations is: magnification, mirroring at x axis,\n"
@ -717,7 +702,7 @@ struct cplx_trans_defs
"@param mirrx True, if mirrored at x axis\n"
"@param u The displacement\n"
) +
constructor ("new", &new_mrmxy, arg ("mag"), arg ("rot"), arg ("mirrx"), arg ("x"), arg ("y"),
constructor ("new", &new_mrmxy, arg ("mag", 1.0), arg ("rot", 0.0), arg ("mirrx", false), arg ("x", 0), arg ("y", 0),
"@brief Creates a transformation using magnification, angle, mirror flag and displacement\n"
"\n"
"The sequence of operations is: magnification, mirroring at x axis,\n"

View File

@ -22,6 +22,7 @@ SOURCES = \
gsiTypes.cc \
gsiSignals.cc \
gsiObjectHolder.cc \
gsiVariantArgs.cc
HEADERS = \
gsiCallback.h \
@ -43,7 +44,8 @@ HEADERS = \
gsiSignals.h \
gsiTypes.h \
gsiObjectHolder.h \
gsiCommon.h
gsiCommon.h \
gsiVariantArgs.h
# Note: unlike other modules, the tl declarations have to go here
# since gsi is dependent on tl

View File

@ -24,6 +24,7 @@
#include "gsiDecl.h"
#include "gsiExpression.h"
#include "gsiObjectHolder.h"
#include "gsiVariantArgs.h"
#include "tlExpression.h"
#include "tlLog.h"
@ -252,874 +253,6 @@ void *get_object_raw (tl::Variant &var)
return obj;
}
// -------------------------------------------------------------------
// Test if an argument can be converted to the given type
bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose);
template <class R>
struct test_arg_func
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/)
{
*ret = arg.can_convert_to<R> ();
}
};
template <>
struct test_arg_func<gsi::VoidType>
{
void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/)
{
*ret = true;
}
};
template <>
struct test_arg_func<gsi::ObjectType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
{
// allow nil of pointers
if ((atype.is_ptr () || atype.is_cptr ()) && arg.is_nil ()) {
*ret = true;
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 = int (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;
}
const tl::VariantUserClassBase *cls = arg.user_cls ();
if (! cls) {
*ret = false;
} else if (! cls->gsi_cls ()->is_derived_from (atype.cls ()) && (! loose || ! cls->gsi_cls ()->can_convert_to(atype.cls ()))) {
*ret = false;
} else if ((atype.is_ref () || atype.is_ptr ()) && cls->is_const ()) {
*ret = false;
} else {
*ret = true;
}
}
};
template <>
struct test_arg_func<gsi::VectorType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
{
if (! arg.is_list ()) {
*ret = false;
return;
}
tl_assert (atype.inner () != 0);
const ArgType &ainner = *atype.inner ();
*ret = true;
for (tl::Variant::const_iterator v = arg.begin (); v != arg.end () && *ret; ++v) {
if (! test_arg (ainner, *v, loose)) {
*ret = false;
}
}
}
};
template <>
struct test_arg_func<gsi::MapType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
{
// Note: delegating that to the function avoids "injected class name used as template template expression" warning
if (! arg.is_array ()) {
*ret = false;
return;
}
tl_assert (atype.inner () != 0);
tl_assert (atype.inner_k () != 0);
const ArgType &ainner = *atype.inner ();
const ArgType &ainner_k = *atype.inner_k ();
if (! arg.is_list ()) {
*ret = false;
return;
}
*ret = true;
for (tl::Variant::const_array_iterator a = arg.begin_array (); a != arg.end_array () && *ret; ++a) {
if (! test_arg (ainner_k, a->first, loose)) {
*ret = false;
} else if (! test_arg (ainner, a->second, loose)) {
*ret = false;
}
}
}
};
bool
test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose)
{
// for const X * or X *, nil is an allowed value
if ((atype.is_cptr () || atype.is_ptr ()) && arg.is_nil ()) {
return true;
}
bool ret = false;
gsi::do_on_type<test_arg_func> () (atype.type (), &ret, arg, atype, loose);
return ret;
}
// -------------------------------------------------------------------
// Variant to C conversion
template <class R>
struct var2c
{
static R get (const tl::Variant &rval)
{
return rval.to<R> ();
}
};
template <>
struct var2c<tl::Variant>
{
static const tl::Variant &get (const tl::Variant &rval)
{
return rval;
}
};
// ---------------------------------------------------------------------
// Serialization helpers
/**
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
*/
class VariantBasedVectorAdaptorIterator
: public gsi::VectorAdaptorIterator
{
public:
VariantBasedVectorAdaptorIterator (tl::Variant::iterator b, tl::Variant::iterator e, const gsi::ArgType *ainner);
virtual void get (SerialArgs &w, tl::Heap &heap) const;
virtual bool at_end () const;
virtual void inc ();
private:
tl::Variant::iterator m_b, m_e;
const gsi::ArgType *mp_ainner;
};
/**
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
*/
class VariantBasedVectorAdaptor
: public gsi::VectorAdaptor
{
public:
VariantBasedVectorAdaptor (tl::Variant *var, const gsi::ArgType *ainner);
virtual VectorAdaptorIterator *create_iterator () const;
virtual void push (SerialArgs &r, tl::Heap &heap);
virtual void clear ();
virtual size_t size () const;
virtual size_t serial_size () const;
private:
const gsi::ArgType *mp_ainner;
tl::Variant *mp_var;
};
/**
* @brief An adaptor for a map which uses the tl::Variant's array perspective
*/
class VariantBasedMapAdaptorIterator
: public gsi::MapAdaptorIterator
{
public:
VariantBasedMapAdaptorIterator (tl::Variant::array_iterator b, tl::Variant::array_iterator e, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
virtual void get (SerialArgs &w, tl::Heap &heap) const;
virtual bool at_end () const;
virtual void inc ();
private:
tl::Variant::array_iterator m_b, m_e;
const gsi::ArgType *mp_ainner, *mp_ainner_k;
};
/**
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
*/
class VariantBasedMapAdaptor
: public gsi::MapAdaptor
{
public:
VariantBasedMapAdaptor (tl::Variant *var, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
virtual MapAdaptorIterator *create_iterator () const;
virtual void insert (SerialArgs &r, tl::Heap &heap);
virtual void clear ();
virtual size_t size () const;
virtual size_t serial_size () const;
private:
const gsi::ArgType *mp_ainner, *mp_ainner_k;
tl::Variant *mp_var;
};
// ---------------------------------------------------------------------
// Writer function for serialization
/**
* @brief Serialization of POD types
*/
template <class R>
struct writer
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap)
{
if (arg->is_nil () && atype.type () != gsi::T_var) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
throw tl::Exception (tl::to_string (tr ("Arguments of reference or direct type cannot be passed nil")));
} else if (atype.is_ptr ()) {
aa->write<R *> ((R *)0);
} else {
aa->write<const R *> ((const R *)0);
}
} else {
if (atype.is_ref () || atype.is_ptr ()) {
// TODO: morph the variant to the requested type and pass its pointer (requires a non-const reference for arg)
// -> we would have a reference that can modify the argument (out parameter).
R *v = new R (var2c<R>::get (*arg));
heap->push (v);
aa->write<void *> (v);
} else if (atype.is_cref ()) {
// Note: POD's are written as copies for const refs, so we can pass a temporary here:
// (avoids having to create a temp object)
aa->write<const R &> (var2c<R>::get (*arg));
} else if (atype.is_cptr ()) {
// Note: POD's are written as copies for const ptrs, so we can pass a temporary here:
// (avoids having to create a temp object)
R r = var2c<R>::get (*arg);
aa->write<const R *> (&r);
} else {
aa->write<R> (var2c<R>::get (*arg));
}
}
}
};
/**
* @brief Serialization for strings
*/
template <>
struct writer<StringType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
{
// Cannot pass ownership currently
tl_assert (!atype.pass_obj ());
if (arg->is_nil ()) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
// nil is treated as an empty string for references
aa->write<void *> ((void *)new StringAdaptorImpl<std::string> (std::string ()));
} else {
aa->write<void *> ((void *)0);
}
} else {
// TODO: morph the variant to the requested type and pass its pointer (requires a non-const reference for arg)
// -> we would have a reference that can modify the argument (out parameter).
// NOTE: by convention we pass the ownership to the receiver for adaptors.
aa->write<void *> ((void *)new StringAdaptorImpl<std::string> (arg->to_string ()));
}
}
};
/**
* @brief Specialization for Variant
*/
template <>
struct writer<VariantType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &, tl::Heap *)
{
// TODO: clarify: is nil a zero-pointer to a variant or a pointer to a "nil" variant?
// NOTE: by convention we pass the ownership to the receiver for adaptors.
aa->write<void *> ((void *)new VariantAdaptorImpl<tl::Variant> (arg));
}
};
/**
* @brief Specialization for Vectors
*/
template <>
struct writer<VectorType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
{
if (arg->is_nil ()) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
throw tl::Exception (tl::to_string (tr ("Arguments of reference or direct type cannot be passed nil")));
} else {
aa->write<void *> ((void *)0);
}
} else {
tl_assert (atype.inner () != 0);
aa->write<void *> ((void *)new VariantBasedVectorAdaptor (arg, atype.inner ()));
}
}
};
/**
* @brief Specialization for Maps
*/
template <>
struct writer<MapType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
{
if (arg->is_nil ()) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
throw tl::Exception (tl::to_string (tr ("Arguments of reference or direct type cannot be passed nil")));
} else {
aa->write<void *> ((void *)0);
}
} else {
tl_assert (atype.inner () != 0);
tl_assert (atype.inner_k () != 0);
aa->write<void *> ((void *)new VariantBasedMapAdaptor (arg, atype.inner (), atype.inner_k ()));
}
}
};
/**
* @brief Specialization for void
*/
template <>
struct writer<gsi::VoidType>
{
void operator() (gsi::SerialArgs *, tl::Variant *, const gsi::ArgType &, tl::Heap *)
{
// nothing - void type won't be serialized
}
};
void push_args (gsi::SerialArgs &arglist, const tl::Variant &args, const gsi::MethodBase *meth, tl::Heap *heap);
/**
* @brief Specialization for void
*/
template <>
struct writer<gsi::ObjectType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap)
{
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")));
}
aa->write<void *> ((void *) 0);
} else 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 = 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)) {
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, *arg, meth, heap);
meth->call (0, arglist, retlist);
void *new_obj = retlist.read<void *> (*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<void *> (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 ())) {
if (cls->gsi_cls ()->adapted_type_info ()) {
// resolved adapted type
aa->write<void *> ((void *) cls->gsi_cls ()->adapted_from_obj (get_object (*arg)));
} else {
aa->write<void *> (get_object (*arg));
}
} else if ((atype.is_cref () || atype.is_cptr ()) && cls->gsi_cls ()->can_convert_to (atype.cls ())) {
// 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 (cls->gsi_cls (), get_object (*arg));
heap->push (new gsi::ObjectHolder (atype.cls (), new_obj));
aa->write<void *> (new_obj);
} else {
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 ()->is_derived_from (atype.cls ())) {
if (cls->gsi_cls ()->adapted_type_info ()) {
aa->write<void *> (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg)));
} else {
aa->write<void *> ((void *) cls->gsi_cls ()->clone (get_object (*arg)));
}
} else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) {
aa->write<void *> (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 ()));
}
}
}
}
};
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<writer> () (a->type (), &arglist, const_cast<tl::Variant *> ((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
/**
* @brief A reader function
*/
template <class R>
struct reader
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
if (atype.is_ref ()) {
*out = rr->template read<R &> (*heap);
} else if (atype.is_cref ()) {
*out = rr->template read<const R &> (*heap);
} else if (atype.is_ptr ()) {
R *p = rr->template read<R *> (*heap);
if (p == 0) {
*out = tl::Variant ();
} else {
*out = *p;
}
} else if (atype.is_cptr ()) {
const R *p = rr->template read<const R *> (*heap);
if (p == 0) {
*out = tl::Variant ();
} else {
*out = *p;
}
} else {
*out = rr->template read<R> (*heap);
}
}
};
/**
* @brief A reader specialization for void *
*/
template <>
struct reader<void *>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
tl_assert (!atype.is_ref ());
tl_assert (!atype.is_cref ());
tl_assert (!atype.is_ptr ());
tl_assert (!atype.is_cptr ());
*out = size_t (rr->read<void *> (*heap));
}
};
/**
* @brief A reader specialization for strings
*/
template <>
struct reader<gsi::StringType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &, tl::Heap *heap)
{
std::unique_ptr<StringAdaptor> a ((StringAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
*out = tl::Variant (std::string (a->c_str (), a->size ()));
}
}
};
/**
* @brief A reader specialization for variants
*/
template <>
struct reader<gsi::VariantType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &, tl::Heap *heap)
{
std::unique_ptr<VariantAdaptor> a ((VariantAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
*out = a->var ();
}
}
};
/**
* @brief A reader specialization for maps
*/
template <>
struct reader<MapType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
std::unique_ptr<MapAdaptor> a ((MapAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
tl_assert (atype.inner () != 0);
tl_assert (atype.inner_k () != 0);
VariantBasedMapAdaptor t (out, atype.inner (), atype.inner_k ());
a->copy_to (&t, *heap);
}
}
};
/**
* @brief A reader specialization for const char *
*/
template <>
struct reader<VectorType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
std::unique_ptr<VectorAdaptor> a ((VectorAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
tl_assert (atype.inner () != 0);
VariantBasedVectorAdaptor t (out, atype.inner ());
a->copy_to (&t, *heap);
}
}
};
/**
* @brief A reader specialization for objects
*/
template <>
struct reader<ObjectType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
void *obj = rr->read<void *> (*heap);
bool is_const = atype.is_cptr () || atype.is_cref ();
bool owner = true;
if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) {
owner = atype.pass_obj ();
}
bool can_destroy = atype.is_ptr () || owner;
const gsi::ClassBase *clsact = atype.cls ()->subclass_decl (obj);
tl_assert (clsact != 0);
if (obj == 0) {
*out = tl::Variant ();
} else if (!clsact->adapted_type_info () && clsact->is_managed ()) {
// gsi::ObjectBase-based objects can be managed by reference since they
// provide a tl::Object through the proxy.
*out = tl::Variant ();
const tl::VariantUserClassBase *cls = clsact->var_cls (atype.is_cref () || atype.is_cptr ());
tl_assert (cls != 0);
Proxy *proxy = clsact->gsi_object (obj)->find_client<Proxy> ();
if (proxy) {
out->set_user_ref (proxy, cls, false);
} else {
// establish a new proxy
proxy = new Proxy (clsact);
proxy->set (obj, owner, is_const, can_destroy);
out->set_user_ref (proxy, cls, owner);
}
} else {
const tl::VariantUserClassBase *cls = 0;
if (clsact->adapted_type_info ()) {
// create an adaptor from an adapted type
if (owner) {
obj = clsact->create_from_adapted_consume (obj);
} else {
obj = clsact->create_from_adapted (obj);
}
cls = clsact->var_cls (false);
} else {
cls = clsact->var_cls (is_const);
}
tl_assert (cls != 0);
*out = tl::Variant ();
// consider prefer_copy
if (! owner && atype.prefer_copy () && !clsact->is_managed () && clsact->can_copy ()) {
obj = clsact->clone (obj);
owner = true;
}
out->set_user (obj, cls, owner);
}
}
};
/**
* @brief A reader specialization for new objects
*/
template <>
struct reader<VoidType>
{
void
operator() (tl::Variant *, gsi::SerialArgs *, const gsi::ArgType &, tl::Heap *)
{
// nothing - void type won't be serialized
}
};
// ---------------------------------------------------------------------
// VariantBasedVectorAdaptorIterator implementation
VariantBasedVectorAdaptorIterator::VariantBasedVectorAdaptorIterator (tl::Variant::iterator b, tl::Variant::iterator e, const gsi::ArgType *ainner)
: m_b (b), m_e (e), mp_ainner (ainner)
{
// .. nothing yet ..
}
void VariantBasedVectorAdaptorIterator::get (SerialArgs &w, tl::Heap &heap) const
{
gsi::do_on_type<writer> () (mp_ainner->type (), &w, &*m_b, *mp_ainner, &heap);
}
bool VariantBasedVectorAdaptorIterator::at_end () const
{
return m_b == m_e;
}
void VariantBasedVectorAdaptorIterator::inc ()
{
++m_b;
}
// ---------------------------------------------------------------------
// VariantBasedVectorAdaptor implementation
VariantBasedVectorAdaptor::VariantBasedVectorAdaptor (tl::Variant *var, const gsi::ArgType *ainner)
: mp_ainner (ainner), mp_var (var)
{
}
VectorAdaptorIterator *VariantBasedVectorAdaptor::create_iterator () const
{
return new VariantBasedVectorAdaptorIterator (mp_var->begin (), mp_var->end (), mp_ainner);
}
void VariantBasedVectorAdaptor::push (SerialArgs &r, tl::Heap &heap)
{
tl::Variant member;
gsi::do_on_type<reader> () (mp_ainner->type (), &member, &r, *mp_ainner, &heap);
mp_var->push (member);
}
void VariantBasedVectorAdaptor::clear ()
{
mp_var->set_list ();
}
size_t VariantBasedVectorAdaptor::size () const
{
return mp_var->size ();
}
size_t VariantBasedVectorAdaptor::serial_size () const
{
return mp_ainner->size ();
}
// ---------------------------------------------------------------------
// VariantBasedMapAdaptorIterator implementation
VariantBasedMapAdaptorIterator::VariantBasedMapAdaptorIterator (tl::Variant::array_iterator b, tl::Variant::array_iterator e, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
: m_b (b), m_e (e), mp_ainner (ainner), mp_ainner_k (ainner_k)
{
// .. nothing yet ..
}
void VariantBasedMapAdaptorIterator::get (SerialArgs &w, tl::Heap &heap) const
{
// Note: the const_cast is ugly but in this context we won't modify the variant given as the key.
// And it lets us keep the interface tidy.
gsi::do_on_type<writer> () (mp_ainner_k->type (), &w, const_cast<tl::Variant *> (&m_b->first), *mp_ainner_k, &heap);
gsi::do_on_type<writer> () (mp_ainner->type (), &w, &m_b->second, *mp_ainner, &heap);
}
bool VariantBasedMapAdaptorIterator::at_end () const
{
return m_b == m_e;
}
void VariantBasedMapAdaptorIterator::inc ()
{
++m_b;
}
// ---------------------------------------------------------------------
// VariantBasedMapAdaptor implementation
VariantBasedMapAdaptor::VariantBasedMapAdaptor (tl::Variant *var, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
: mp_ainner (ainner), mp_ainner_k (ainner_k), mp_var (var)
{
}
MapAdaptorIterator *VariantBasedMapAdaptor::create_iterator () const
{
return new VariantBasedMapAdaptorIterator (mp_var->begin_array (), mp_var->end_array (), mp_ainner, mp_ainner_k);
}
void VariantBasedMapAdaptor::insert (SerialArgs &r, tl::Heap &heap)
{
tl::Variant k, v;
gsi::do_on_type<reader> () (mp_ainner_k->type (), &k, &r, *mp_ainner_k, &heap);
gsi::do_on_type<reader> () (mp_ainner->type (), &v, &r, *mp_ainner, &heap);
mp_var->insert (k, v);
}
void VariantBasedMapAdaptor::clear ()
{
mp_var->set_array ();
}
size_t VariantBasedMapAdaptor::size () const
{
return mp_var->array_size ();
}
size_t VariantBasedMapAdaptor::serial_size () const
{
return mp_ainner_k->size () + mp_ainner->size ();
}
// ---------------------------------------------------------------------
// Implementation of initialize_expressions
@ -1671,7 +804,7 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context
int sc = 0;
int i = 0;
for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); is_valid && i < int (args.size ()) && a != (*m)->end_arguments (); ++a, ++i) {
if (test_arg (*a, args [i], false /*strict*/)) {
if (gsi::test_arg (*a, args [i], false /*strict*/)) {
++sc;
} else if (test_arg (*a, args [i], true /*loose*/)) {
// non-scoring match
@ -1747,7 +880,7 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context
try {
// Note: this const_cast is ugly, but it will basically enable "out" parameters
// TODO: clean this up.
gsi::do_on_type<writer> () (a->type (), &arglist, const_cast<tl::Variant *> (&args [narg]), *a, &heap);
gsi::push_arg (arglist, *a, const_cast<tl::Variant &> (args [narg]), &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);
@ -1764,7 +897,7 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context
} else {
out = tl::Variant ();
try {
gsi::do_on_type<reader> () (meth->ret_type ().type (), &out, &retlist, meth->ret_type (), &heap);
gsi::pull_arg (retlist, meth->ret_type (), out, &heap);
} catch (tl::Exception &ex) {
std::string msg = ex.msg () + tl::to_string (tr (" (return value)"));
throw tl::Exception (msg);

View File

@ -132,16 +132,132 @@ void MethodBase::parse_name (const std::string &name)
}
}
static std::string
type_to_s (const gsi::ArgType &a, bool for_return)
{
std::string s;
switch (a.type ()) {
case gsi::T_void_ptr:
s += "void *"; break;
case gsi::T_void:
s += "void"; break;
case gsi::T_bool:
s += "bool"; break;
case gsi::T_char:
s += "char"; break;
case gsi::T_schar:
s += "signed char"; break;
case gsi::T_uchar:
s += "unsigned char"; break;
case gsi::T_short:
s += "short"; break;
case gsi::T_ushort:
s += "unsigned short"; break;
case gsi::T_int:
s += "int"; break;
#if defined(HAVE_64BIT_COORD)
case gsi::T_int128:
s += "int128"; break;
#endif
case gsi::T_uint:
s += "unsigned int"; break;
case gsi::T_long:
s += "long"; break;
case gsi::T_ulong:
s += "unsigned long"; break;
case gsi::T_longlong:
s += "long long"; break;
case gsi::T_ulonglong:
s += "unsigned long long"; break;
case gsi::T_double:
s += "double"; break;
case gsi::T_float:
s += "float"; break;
case gsi::T_string:
s += "string"; break;
case gsi::T_byte_array:
s += "bytes"; break;
case gsi::T_var:
s += "variant"; break;
case gsi::T_object:
if (a.is_cptr () || (! for_return && a.is_cref ())) {
s = "const ";
}
if (a.pass_obj ()) {
s += "new ";
}
s += a.cls () ? a.cls ()->qname () : "?";
break;
case gsi::T_vector:
if (a.inner ()) {
s += type_to_s (*a.inner (), false);
}
s += "[]";
break;
case gsi::T_map:
s += "map&lt;";
if (a.inner_k ()) {
s += type_to_s (*a.inner_k (), false);
}
s += ",";
if (a.inner ()) {
s += type_to_s (*a.inner (), false);
}
s += "&gt;";
break;
}
if (a.is_cptr () || a.is_ptr ()) {
s += " ptr";
}
return s;
}
static std::string
method_attributes (const gsi::MethodBase *method)
{
std::string r;
if (method->is_signal ()) {
if (! r.empty ()) {
r += ",";
}
r += "signal";
}
if (method->is_callback ()) {
if (! r.empty ()) {
r += ",";
}
r += "virtual";
}
if (method->is_static ()) {
if (! r.empty ()) {
r += ",";
}
r += "static";
}
if (method->is_const ()) {
if (! r.empty ()) {
r += ",";
}
r += "const";
}
if (method->ret_type ().is_iter ()) {
if (! r.empty ()) {
r += ",";
}
r += "iter";
}
return r;
}
std::string
MethodBase::to_string () const
{
std::string res;
if (is_static ()) {
res += "static ";
std::string res = method_attributes (this);
if (! res.empty ()) {
res += " ";
}
res += ret_type ().to_string ();
res += type_to_s (ret_type (), true);
res += " ";
if (m_method_synonyms.size () == 1) {
@ -155,7 +271,24 @@ MethodBase::to_string () const
if (a != begin_arguments ()) {
res += ", ";
}
res += a->to_string ();
res += type_to_s (*a, false);
if (! a->spec ()->name ().empty ()) {
res += " ";
res += a->spec ()->name ();
}
if (a->spec ()->has_default ()) {
res += " = ";
if (! a->spec ()->init_doc ().empty ()) {
res += a->spec ()->init_doc ();
} else {
try {
res += a->spec ()->default_value ().to_string ();
} catch (tl::Exception &) {
res += "?";
}
}
}
}
res += ")";

View File

@ -0,0 +1,928 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2023 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gsiVariantArgs.h"
#include "gsiTypes.h"
#include "gsiSerialisation.h"
#include "gsiClassBase.h"
#include "gsiObjectHolder.h"
#include "tlVariant.h"
#include "tlHeap.h"
namespace gsi
{
// -------------------------------------------------------------------
/**
* @brief Fetches the final object pointer from a tl::Variant
*/
inline void *get_object (tl::Variant &var)
{
return var.to_user ();
}
// -------------------------------------------------------------------
// Test if an argument can be converted to the given type
bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose);
template <class R>
struct test_arg_func
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/)
{
*ret = arg.can_convert_to<R> ();
}
};
template <>
struct test_arg_func<gsi::VoidType>
{
void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/)
{
*ret = true;
}
};
template <>
struct test_arg_func<gsi::ObjectType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
{
// allow nil of pointers
if ((atype.is_ptr () || atype.is_cptr ()) && arg.is_nil ()) {
*ret = true;
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 = int (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;
}
const tl::VariantUserClassBase *cls = arg.user_cls ();
if (! cls) {
*ret = false;
} else if (! cls->gsi_cls ()->is_derived_from (atype.cls ()) && (! loose || ! cls->gsi_cls ()->can_convert_to(atype.cls ()))) {
*ret = false;
} else if ((atype.is_ref () || atype.is_ptr ()) && cls->is_const ()) {
*ret = false;
} else {
*ret = true;
}
}
};
template <>
struct test_arg_func<gsi::VectorType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
{
if (! arg.is_list ()) {
*ret = false;
return;
}
tl_assert (atype.inner () != 0);
const ArgType &ainner = *atype.inner ();
*ret = true;
for (tl::Variant::const_iterator v = arg.begin (); v != arg.end () && *ret; ++v) {
if (! test_arg (ainner, *v, loose)) {
*ret = false;
}
}
}
};
template <>
struct test_arg_func<gsi::MapType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
{
// Note: delegating that to the function avoids "injected class name used as template template expression" warning
if (! arg.is_array ()) {
*ret = false;
return;
}
tl_assert (atype.inner () != 0);
tl_assert (atype.inner_k () != 0);
const ArgType &ainner = *atype.inner ();
const ArgType &ainner_k = *atype.inner_k ();
if (! arg.is_list ()) {
*ret = false;
return;
}
*ret = true;
for (tl::Variant::const_array_iterator a = arg.begin_array (); a != arg.end_array () && *ret; ++a) {
if (! test_arg (ainner_k, a->first, loose)) {
*ret = false;
} else if (! test_arg (ainner, a->second, loose)) {
*ret = false;
}
}
}
};
bool
test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose)
{
// for const X * or X *, nil is an allowed value
if ((atype.is_cptr () || atype.is_ptr ()) && arg.is_nil ()) {
return true;
}
bool ret = false;
gsi::do_on_type<test_arg_func> () (atype.type (), &ret, arg, atype, loose);
return ret;
}
// -------------------------------------------------------------------
// Variant to C conversion
template <class R>
struct var2c
{
static R get (const tl::Variant &rval)
{
return rval.to<R> ();
}
};
template <>
struct var2c<tl::Variant>
{
static const tl::Variant &get (const tl::Variant &rval)
{
return rval;
}
};
// ---------------------------------------------------------------------
// Serialization helpers
/**
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
*/
class VariantBasedVectorAdaptorIterator
: public gsi::VectorAdaptorIterator
{
public:
VariantBasedVectorAdaptorIterator (tl::Variant::iterator b, tl::Variant::iterator e, const gsi::ArgType *ainner);
virtual void get (SerialArgs &w, tl::Heap &heap) const;
virtual bool at_end () const;
virtual void inc ();
private:
tl::Variant::iterator m_b, m_e;
const gsi::ArgType *mp_ainner;
};
/**
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
*/
class VariantBasedVectorAdaptor
: public gsi::VectorAdaptor
{
public:
VariantBasedVectorAdaptor (tl::Variant *var, const gsi::ArgType *ainner);
virtual VectorAdaptorIterator *create_iterator () const;
virtual void push (SerialArgs &r, tl::Heap &heap);
virtual void clear ();
virtual size_t size () const;
virtual size_t serial_size () const;
private:
const gsi::ArgType *mp_ainner;
tl::Variant *mp_var;
};
/**
* @brief An adaptor for a map which uses the tl::Variant's array perspective
*/
class VariantBasedMapAdaptorIterator
: public gsi::MapAdaptorIterator
{
public:
VariantBasedMapAdaptorIterator (tl::Variant::array_iterator b, tl::Variant::array_iterator e, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
virtual void get (SerialArgs &w, tl::Heap &heap) const;
virtual bool at_end () const;
virtual void inc ();
private:
tl::Variant::array_iterator m_b, m_e;
const gsi::ArgType *mp_ainner, *mp_ainner_k;
};
/**
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
*/
class VariantBasedMapAdaptor
: public gsi::MapAdaptor
{
public:
VariantBasedMapAdaptor (tl::Variant *var, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
virtual MapAdaptorIterator *create_iterator () const;
virtual void insert (SerialArgs &r, tl::Heap &heap);
virtual void clear ();
virtual size_t size () const;
virtual size_t serial_size () const;
private:
const gsi::ArgType *mp_ainner, *mp_ainner_k;
tl::Variant *mp_var;
};
// ---------------------------------------------------------------------
// Writer function for serialization
/**
* @brief Serialization of POD types
*/
template <class R>
struct writer
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap)
{
if (arg->is_nil () && atype.type () != gsi::T_var) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
throw tl::Exception (tl::to_string (tr ("Arguments of reference or direct type cannot be passed nil")));
} else if (atype.is_ptr ()) {
aa->write<R *> ((R *)0);
} else {
aa->write<const R *> ((const R *)0);
}
} else {
if (atype.is_ref () || atype.is_ptr ()) {
// TODO: morph the variant to the requested type and pass its pointer (requires a non-const reference for arg)
// -> we would have a reference that can modify the argument (out parameter).
R *v = new R (var2c<R>::get (*arg));
heap->push (v);
aa->write<void *> (v);
} else if (atype.is_cref ()) {
// Note: POD's are written as copies for const refs, so we can pass a temporary here:
// (avoids having to create a temp object)
aa->write<const R &> (var2c<R>::get (*arg));
} else if (atype.is_cptr ()) {
// Note: POD's are written as copies for const ptrs, so we can pass a temporary here:
// (avoids having to create a temp object)
R r = var2c<R>::get (*arg);
aa->write<const R *> (&r);
} else {
aa->write<R> (var2c<R>::get (*arg));
}
}
}
};
/**
* @brief Serialization for strings
*/
template <>
struct writer<StringType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
{
// Cannot pass ownership currently
tl_assert (!atype.pass_obj ());
if (arg->is_nil ()) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
// nil is treated as an empty string for references
aa->write<void *> ((void *)new StringAdaptorImpl<std::string> (std::string ()));
} else {
aa->write<void *> ((void *)0);
}
} else {
// TODO: morph the variant to the requested type and pass its pointer (requires a non-const reference for arg)
// -> we would have a reference that can modify the argument (out parameter).
// NOTE: by convention we pass the ownership to the receiver for adaptors.
aa->write<void *> ((void *)new StringAdaptorImpl<std::string> (arg->to_string ()));
}
}
};
/**
* @brief Specialization for Variant
*/
template <>
struct writer<VariantType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &, tl::Heap *)
{
// TODO: clarify: is nil a zero-pointer to a variant or a pointer to a "nil" variant?
// NOTE: by convention we pass the ownership to the receiver for adaptors.
aa->write<void *> ((void *)new VariantAdaptorImpl<tl::Variant> (arg));
}
};
/**
* @brief Specialization for Vectors
*/
template <>
struct writer<VectorType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
{
if (arg->is_nil ()) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
throw tl::Exception (tl::to_string (tr ("Arguments of reference or direct type cannot be passed nil")));
} else {
aa->write<void *> ((void *)0);
}
} else {
tl_assert (atype.inner () != 0);
aa->write<void *> ((void *)new VariantBasedVectorAdaptor (arg, atype.inner ()));
}
}
};
/**
* @brief Specialization for Maps
*/
template <>
struct writer<MapType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
{
if (arg->is_nil ()) {
if (! (atype.is_ptr () || atype.is_cptr ())) {
throw tl::Exception (tl::to_string (tr ("Arguments of reference or direct type cannot be passed nil")));
} else {
aa->write<void *> ((void *)0);
}
} else {
tl_assert (atype.inner () != 0);
tl_assert (atype.inner_k () != 0);
aa->write<void *> ((void *)new VariantBasedMapAdaptor (arg, atype.inner (), atype.inner_k ()));
}
}
};
/**
* @brief Specialization for void
*/
template <>
struct writer<gsi::VoidType>
{
void operator() (gsi::SerialArgs *, tl::Variant *, const gsi::ArgType &, tl::Heap *)
{
// nothing - void type won't be serialized
}
};
void push_args (gsi::SerialArgs &arglist, const tl::Variant &args, const gsi::MethodBase *meth, tl::Heap *heap);
/**
* @brief Specialization for void
*/
template <>
struct writer<gsi::ObjectType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap)
{
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")));
}
aa->write<void *> ((void *) 0);
} else 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 = 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)) {
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, *arg, meth, heap);
meth->call (0, arglist, retlist);
void *new_obj = retlist.read<void *> (*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<void *> (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 ())) {
if (cls->gsi_cls ()->adapted_type_info ()) {
// resolved adapted type
aa->write<void *> ((void *) cls->gsi_cls ()->adapted_from_obj (get_object (*arg)));
} else {
aa->write<void *> (get_object (*arg));
}
} else if ((atype.is_cref () || atype.is_cptr ()) && cls->gsi_cls ()->can_convert_to (atype.cls ())) {
// 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 (cls->gsi_cls (), get_object (*arg));
heap->push (new gsi::ObjectHolder (atype.cls (), new_obj));
aa->write<void *> (new_obj);
} else {
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 ()->is_derived_from (atype.cls ())) {
if (cls->gsi_cls ()->adapted_type_info ()) {
aa->write<void *> (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg)));
} else {
aa->write<void *> ((void *) cls->gsi_cls ()->clone (get_object (*arg)));
}
} else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) {
aa->write<void *> (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 ()));
}
}
}
}
};
void push_arg (gsi::SerialArgs &arglist, const ArgType &atype, tl::Variant &arg, tl::Heap *heap)
{
gsi::do_on_type<writer> () (atype.type (), &arglist, &arg, atype, heap);
}
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<writer> () (a->type (), &arglist, const_cast<tl::Variant *> ((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
/**
* @brief A reader function
*/
template <class R>
struct reader
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
if (atype.is_ref ()) {
*out = rr->template read<R &> (*heap);
} else if (atype.is_cref ()) {
*out = rr->template read<const R &> (*heap);
} else if (atype.is_ptr ()) {
R *p = rr->template read<R *> (*heap);
if (p == 0) {
*out = tl::Variant ();
} else {
*out = *p;
}
} else if (atype.is_cptr ()) {
const R *p = rr->template read<const R *> (*heap);
if (p == 0) {
*out = tl::Variant ();
} else {
*out = *p;
}
} else {
*out = rr->template read<R> (*heap);
}
}
};
/**
* @brief A reader specialization for void *
*/
template <>
struct reader<void *>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
tl_assert (!atype.is_ref ());
tl_assert (!atype.is_cref ());
tl_assert (!atype.is_ptr ());
tl_assert (!atype.is_cptr ());
*out = size_t (rr->read<void *> (*heap));
}
};
/**
* @brief A reader specialization for strings
*/
template <>
struct reader<gsi::StringType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &, tl::Heap *heap)
{
std::unique_ptr<StringAdaptor> a ((StringAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
*out = tl::Variant (std::string (a->c_str (), a->size ()));
}
}
};
/**
* @brief A reader specialization for variants
*/
template <>
struct reader<gsi::VariantType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &, tl::Heap *heap)
{
std::unique_ptr<VariantAdaptor> a ((VariantAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
*out = a->var ();
}
}
};
/**
* @brief A reader specialization for maps
*/
template <>
struct reader<MapType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
std::unique_ptr<MapAdaptor> a ((MapAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
tl_assert (atype.inner () != 0);
tl_assert (atype.inner_k () != 0);
VariantBasedMapAdaptor t (out, atype.inner (), atype.inner_k ());
a->copy_to (&t, *heap);
}
}
};
/**
* @brief A reader specialization for const char *
*/
template <>
struct reader<VectorType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
std::unique_ptr<VectorAdaptor> a ((VectorAdaptor *) rr->read<void *>(*heap));
if (!a.get ()) {
*out = tl::Variant ();
} else {
tl_assert (atype.inner () != 0);
VariantBasedVectorAdaptor t (out, atype.inner ());
a->copy_to (&t, *heap);
}
}
};
/**
* @brief A reader specialization for objects
*/
template <>
struct reader<ObjectType>
{
void
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
{
void *obj = rr->read<void *> (*heap);
bool is_const = atype.is_cptr () || atype.is_cref ();
bool owner = true;
if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) {
owner = atype.pass_obj ();
}
bool can_destroy = atype.is_ptr () || owner;
const gsi::ClassBase *clsact = atype.cls ()->subclass_decl (obj);
tl_assert (clsact != 0);
if (obj == 0) {
*out = tl::Variant ();
} else if (!clsact->adapted_type_info () && clsact->is_managed ()) {
// gsi::ObjectBase-based objects can be managed by reference since they
// provide a tl::Object through the proxy.
*out = tl::Variant ();
const tl::VariantUserClassBase *cls = clsact->var_cls (atype.is_cref () || atype.is_cptr ());
tl_assert (cls != 0);
Proxy *proxy = clsact->gsi_object (obj)->find_client<Proxy> ();
if (proxy) {
out->set_user_ref (proxy, cls, false);
} else {
// establish a new proxy
proxy = new Proxy (clsact);
proxy->set (obj, owner, is_const, can_destroy);
out->set_user_ref (proxy, cls, owner);
}
} else {
const tl::VariantUserClassBase *cls = 0;
if (clsact->adapted_type_info ()) {
// create an adaptor from an adapted type
if (owner) {
obj = clsact->create_from_adapted_consume (obj);
} else {
obj = clsact->create_from_adapted (obj);
}
cls = clsact->var_cls (false);
} else {
cls = clsact->var_cls (is_const);
}
tl_assert (cls != 0);
*out = tl::Variant ();
// consider prefer_copy
if (! owner && atype.prefer_copy () && !clsact->is_managed () && clsact->can_copy ()) {
obj = clsact->clone (obj);
owner = true;
}
out->set_user (obj, cls, owner);
}
}
};
/**
* @brief A reader specialization for new objects
*/
template <>
struct reader<VoidType>
{
void
operator() (tl::Variant *, gsi::SerialArgs *, const gsi::ArgType &, tl::Heap *)
{
// nothing - void type won't be serialized
}
};
// ---------------------------------------------------------------------
// VariantBasedVectorAdaptorIterator implementation
VariantBasedVectorAdaptorIterator::VariantBasedVectorAdaptorIterator (tl::Variant::iterator b, tl::Variant::iterator e, const gsi::ArgType *ainner)
: m_b (b), m_e (e), mp_ainner (ainner)
{
// .. nothing yet ..
}
void VariantBasedVectorAdaptorIterator::get (SerialArgs &w, tl::Heap &heap) const
{
gsi::do_on_type<writer> () (mp_ainner->type (), &w, &*m_b, *mp_ainner, &heap);
}
bool VariantBasedVectorAdaptorIterator::at_end () const
{
return m_b == m_e;
}
void VariantBasedVectorAdaptorIterator::inc ()
{
++m_b;
}
// ---------------------------------------------------------------------
// VariantBasedVectorAdaptor implementation
VariantBasedVectorAdaptor::VariantBasedVectorAdaptor (tl::Variant *var, const gsi::ArgType *ainner)
: mp_ainner (ainner), mp_var (var)
{
}
VectorAdaptorIterator *VariantBasedVectorAdaptor::create_iterator () const
{
return new VariantBasedVectorAdaptorIterator (mp_var->begin (), mp_var->end (), mp_ainner);
}
void VariantBasedVectorAdaptor::push (SerialArgs &r, tl::Heap &heap)
{
tl::Variant member;
gsi::do_on_type<reader> () (mp_ainner->type (), &member, &r, *mp_ainner, &heap);
mp_var->push (member);
}
void VariantBasedVectorAdaptor::clear ()
{
mp_var->set_list ();
}
size_t VariantBasedVectorAdaptor::size () const
{
return mp_var->size ();
}
size_t VariantBasedVectorAdaptor::serial_size () const
{
return mp_ainner->size ();
}
// ---------------------------------------------------------------------
// VariantBasedMapAdaptorIterator implementation
VariantBasedMapAdaptorIterator::VariantBasedMapAdaptorIterator (tl::Variant::array_iterator b, tl::Variant::array_iterator e, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
: m_b (b), m_e (e), mp_ainner (ainner), mp_ainner_k (ainner_k)
{
// .. nothing yet ..
}
void VariantBasedMapAdaptorIterator::get (SerialArgs &w, tl::Heap &heap) const
{
// Note: the const_cast is ugly but in this context we won't modify the variant given as the key.
// And it lets us keep the interface tidy.
gsi::do_on_type<writer> () (mp_ainner_k->type (), &w, const_cast<tl::Variant *> (&m_b->first), *mp_ainner_k, &heap);
gsi::do_on_type<writer> () (mp_ainner->type (), &w, &m_b->second, *mp_ainner, &heap);
}
bool VariantBasedMapAdaptorIterator::at_end () const
{
return m_b == m_e;
}
void VariantBasedMapAdaptorIterator::inc ()
{
++m_b;
}
// ---------------------------------------------------------------------
// VariantBasedMapAdaptor implementation
VariantBasedMapAdaptor::VariantBasedMapAdaptor (tl::Variant *var, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
: mp_ainner (ainner), mp_ainner_k (ainner_k), mp_var (var)
{
}
MapAdaptorIterator *VariantBasedMapAdaptor::create_iterator () const
{
return new VariantBasedMapAdaptorIterator (mp_var->begin_array (), mp_var->end_array (), mp_ainner, mp_ainner_k);
}
void VariantBasedMapAdaptor::insert (SerialArgs &r, tl::Heap &heap)
{
tl::Variant k, v;
gsi::do_on_type<reader> () (mp_ainner_k->type (), &k, &r, *mp_ainner_k, &heap);
gsi::do_on_type<reader> () (mp_ainner->type (), &v, &r, *mp_ainner, &heap);
mp_var->insert (k, v);
}
void VariantBasedMapAdaptor::clear ()
{
mp_var->set_array ();
}
size_t VariantBasedMapAdaptor::size () const
{
return mp_var->array_size ();
}
size_t VariantBasedMapAdaptor::serial_size () const
{
return mp_ainner_k->size () + mp_ainner->size ();
}
// ---------------------------------------------------------------------
// pop_arg implementation
void
pull_arg (gsi::SerialArgs &retlist, const gsi::ArgType &atype, tl::Variant &arg_out, tl::Heap *heap)
{
gsi::do_on_type<reader> () (atype.type (), &arg_out, &retlist, atype, heap);
}
}

View File

@ -0,0 +1,81 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2023 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_gsiVariantArgs
#define HDR_gsiVariantArgs
#include "gsiCommon.h"
#include "gsiSerialisation.h"
#include "gsiTypes.h"
namespace tl
{
class Heap;
class Variant;
}
namespace gsi
{
class SerialArgs;
class ArgType;
/**
* @brief Pushes a variant on the serialization stack
*
* This also involves expanding of arrays into objects by calling the constructor.
*
* @param arglist The serialization stack to push the argument to
* @param atype The argument type
* @param arg The argument to push (may be modified if 'out' parameter)
* @param heap The heap
*/
GSI_PUBLIC void push_arg (gsi::SerialArgs &arglist, const gsi::ArgType &atype, tl::Variant &arg, tl::Heap *heap);
/**
* @brief Pulls a variant from the serialization stack
*
* This function will pull the next argument from the bottom of the serialization stack
* and remove it from the stack.
*
* @param retlist The serialization stack
* @param atype The argument type
* @param arg_out Receives the value
* @param heap The heap
*/
GSI_PUBLIC void pull_arg (gsi::SerialArgs &retlist, const gsi::ArgType &atype, tl::Variant &arg_out, tl::Heap *heap);
/**
* @brief Tests if the argument can be passed to a specific type
* @param atype The argument type
* @param arg The value to pass to it
* @param loose true for loose checking
*
* @return True, if the argument can be passed
*/
GSI_PUBLIC bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose);
}
#endif

View File

@ -27,7 +27,9 @@
#include "pyaMarshal.h"
#include "pyaConvert.h"
#include "pyaUtils.h"
#include "gsiMethods.h"
#include "gsiVariantArgs.h"
namespace pya
{
@ -178,15 +180,97 @@ get_return_value (PYAObjectBase *self, gsi::SerialArgs &retlist, const gsi::Meth
} else {
ret = pop_arg (meth->ret_type (), retlist, self, heap).release ();
ret = pull_arg (meth->ret_type (), retlist, self, heap).release ();
}
return ret;
}
inline int
num_args (const gsi::MethodBase *m)
{
return int (m->end_arguments () - m->begin_arguments ());
}
static bool
compatible_with_args (const gsi::MethodBase *m, int argc, PyObject *kwargs)
{
int nargs = num_args (m);
if (argc >= nargs) {
// no more arguments to consider
return argc == nargs && (kwargs == NULL || PyDict_Size (kwargs) == 0);
}
if (kwargs != NULL) {
int nkwargs = int (PyDict_Size (kwargs));
int kwargs_taken = 0;
while (argc < nargs) {
const gsi::ArgType &atype = m->begin_arguments () [argc];
pya::PythonPtr py_arg = PyDict_GetItemString (kwargs, atype.spec ()->name ().c_str ());
if (! py_arg) {
if (! atype.spec ()->has_default ()) {
return false;
}
} else {
++kwargs_taken;
}
++argc;
}
// matches if all keyword arguments are taken
return kwargs_taken == nkwargs;
} else {
while (argc < nargs) {
const gsi::ArgType &atype = m->begin_arguments () [argc];
if (! atype.spec ()->has_default ()) {
return false;
}
++argc;
}
return true;
}
}
static std::string
describe_overload (const gsi::MethodBase *m, int argc, PyObject *kwargs)
{
std::string res = m->to_string ();
if (compatible_with_args (m, argc, kwargs)) {
res += " " + tl::to_string (tr ("[match candidate]"));
}
return res;
}
static std::string
describe_overloads (const MethodTable *mt, int mid, int argc, PyObject *kwargs)
{
std::string res;
for (auto m = mt->begin (mid); m != mt->end (mid); ++m) {
res += std::string (" ") + describe_overload (*m, argc, kwargs) + "\n";
}
return res;
}
static PyObject *
get_kwarg (const gsi::ArgType &atype, PyObject *kwargs)
{
if (kwargs != NULL) {
return PyDict_GetItemString (kwargs, atype.spec ()->name ().c_str ());
} else {
return NULL;
}
}
static const gsi::MethodBase *
match_method (int mid, PyObject *self, PyObject *args, bool strict)
match_method (int mid, PyObject *self, PyObject *args, PyObject *kwargs, bool strict)
{
const gsi::ClassBase *cls_decl = 0;
@ -226,7 +310,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
// ignore callbacks
} else if ((*m)->compatible_with_num_args (argc)) {
} else if (compatible_with_args (*m, argc, kwargs)) {
++candidates;
meth = *m;
@ -237,28 +321,11 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
// no candidate -> error
if (! meth) {
if (! strict) {
return 0;
} else {
throw tl::TypeError (tl::to_string (tr ("Can't match arguments. Variants are:\n")) + describe_overloads (mt, mid, argc, kwargs));
}
std::set<unsigned int> nargs;
for (MethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
if (! (*m)->is_callback ()) {
nargs.insert (std::distance ((*m)->begin_arguments (), (*m)->end_arguments ()));
}
}
std::string nargs_s;
for (std::set<unsigned int>::const_iterator na = nargs.begin (); na != nargs.end (); ++na) {
if (na != nargs.begin ()) {
nargs_s += "/";
}
nargs_s += tl::to_string (*na);
}
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid number of arguments (got %d, expected %s)")), argc, nargs_s));
}
// more than one candidate -> refine by checking the arguments
@ -274,14 +341,18 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
if (! (*m)->is_callback ()) {
// check arguments (count and type)
bool is_valid = (*m)->compatible_with_num_args (argc);
bool is_valid = compatible_with_args (*m, argc, kwargs);
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) {
PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i);
if (test_arg (*a, arg, false /*strict*/)) {
for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); is_valid && a != (*m)->end_arguments (); ++a, ++i) {
PythonPtr arg (i >= argc ? get_kwarg (*a, kwargs) : (is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i)));
if (! arg) {
is_valid = a->spec ()->has_default ();
} else if (test_arg (*a, arg.get (), false /*strict*/)) {
++sc;
} else if (test_arg (*a, arg, true /*loose*/)) {
} else if (test_arg (*a, arg.get (), true /*loose*/)) {
// non-scoring match
} else {
is_valid = false;
@ -306,12 +377,17 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
if (is_valid) {
// otherwise take the candidate with the better score
if (candidates > 0 && sc > score) {
candidates = 1;
meth = *m;
score = sc;
} else if (candidates == 0 || sc == score) {
// otherwise take the candidate with the better score or the least number of arguments (faster)
if (candidates > 0) {
if (sc > score || (sc == score && num_args (meth) > num_args (*m))) {
candidates = 1;
meth = *m;
score = sc;
} else if (sc == score && num_args (meth) == num_args (*m)) {
++candidates;
meth = *m;
}
} else {
++candidates;
meth = *m;
score = sc;
@ -327,9 +403,9 @@ 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) {
PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i);
if (! test_arg (*a, arg, true /*loose*/)) {
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments (); ++a, ++i) {
PythonPtr arg (i >= argc ? get_kwarg (*a, kwargs) : (is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i)));
if (arg && ! test_arg (*a, arg.get (), true /*loose*/)) {
return 0;
}
}
@ -340,7 +416,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
if (! strict || mt->fallback_not_implemented (mid)) {
return 0;
} else {
throw tl::TypeError (tl::to_string (tr ("No overload with matching arguments")));
throw tl::TypeError (tl::to_string (tr ("No overload with matching arguments. Variants are:\n")) + describe_overloads (mt, mid, argc, kwargs));
}
}
@ -348,7 +424,7 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict)
if (! strict || mt->fallback_not_implemented (mid)) {
return 0;
} else {
throw tl::TypeError (tl::to_string (tr ("Ambiguous overload variants - multiple method declarations match arguments")));
throw tl::TypeError (tl::to_string (tr ("Ambiguous overload variants - multiple method declarations match arguments. Variants are:\n")) + describe_overloads (mt, mid, argc, kwargs));
}
}
@ -611,16 +687,64 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P
}
void
push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap)
push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, PyObject *kwargs, tl::Heap &heap)
{
bool is_tuple = PyTuple_Check (args);
int i = 0;
int iarg = 0;
int argc = args == NULL ? 0 : (is_tuple ? int (PyTuple_Size (args)) : int (PyList_Size (args)));
int kwargs_taken = 0;
int nkwargs = kwargs == NULL ? 0 : int (PyDict_Size (kwargs));
try {
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) {
push_arg (*a, arglist, is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i), heap);
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments (); ++a, ++iarg) {
PythonPtr arg (iarg >= argc ? get_kwarg (*a, kwargs) : (is_tuple ? PyTuple_GetItem (args, iarg) : PyList_GetItem (args, iarg)));
if (! arg) {
if (a->spec ()->has_default ()) {
if (kwargs_taken == nkwargs) {
// leave it to the consumer to establish the default values (that is faster)
break;
}
tl::Variant def_value = a->spec ()->default_value ();
gsi::push_arg (arglist, *a, def_value, &heap);
} else {
throw tl::Exception (tl::to_string ("No argument provided (positional or keyword) and no default value available"));
}
} else {
if (iarg >= argc) {
++kwargs_taken;
}
push_arg (*a, arglist, arg.get (), heap);
}
}
if (kwargs_taken != nkwargs) {
// check if there are any left-over keyword parameters with unknown names
pya::PythonRef keys (PyDict_Keys (kwargs));
std::set<std::string> valid_names;
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments (); ++a) {
valid_names.insert (a->spec ()->name ());
}
std::set<std::string> invalid_names;
for (int i = int (PyList_Size (keys.get ())); i > 0; ) {
--i;
std::string k = python2c<std::string> (PyList_GetItem (keys.get (), i));
if (valid_names.find (k) == valid_names.end ()) {
invalid_names.insert (k);
}
}
if (invalid_names.size () > 1) {
std::string names_str = tl::join (invalid_names.begin (), invalid_names.end (), ", ");
throw tl::Exception (tl::to_string (tr ("Unknown keyword parameters: ")) + names_str);
} else if (invalid_names.size () == 1) {
throw tl::Exception (tl::to_string (tr ("Unknown keyword parameter: ")) + *invalid_names.begin ());
}
}
} catch (tl::Exception &ex) {
@ -628,19 +752,26 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args
// In case of an error upon write, pop the arguments to clean them up.
// Without this, there is a risk to keep dead objects on the stack.
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) {
pop_arg (*a, arglist, 0, heap);
pull_arg (*a, arglist, 0, heap);
}
std::string msg;
const gsi::ArgSpecBase *arg_spec = meth->begin_arguments () [i].spec ();
if (iarg < num_args (meth)) {
// attach argument information to the error message if available
std::string msg;
const gsi::ArgSpecBase *arg_spec = meth->begin_arguments () [iarg].spec ();
if (arg_spec && ! arg_spec->name ().empty ()) {
msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d ('%s')")), ex.basic_msg (), iarg + 1, arg_spec->name ());
} else {
msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d")), ex.basic_msg (), iarg + 1);
}
ex.set_basic_msg (msg);
if (arg_spec && ! arg_spec->name ().empty ()) {
msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d ('%s')")), ex.basic_msg (), i + 1, arg_spec->name ());
} else {
msg = tl::sprintf (tl::to_string (tr ("%s for argument #%d")), ex.basic_msg (), i + 1);
}
ex.set_basic_msg (msg);
throw;
} catch (...) {
@ -648,7 +779,7 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args
// In case of an error upon write, pop the arguments to clean them up.
// Without this, there is a risk to keep dead objects on the stack.
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && arglist; ++a) {
pop_arg (*a, arglist, 0, heap);
pull_arg (*a, arglist, 0, heap);
}
throw;
@ -657,13 +788,13 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args
}
static PyObject *
method_adaptor (int mid, PyObject *self, PyObject *args)
method_adaptor (int mid, PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *ret = NULL;
PYA_TRY
const gsi::MethodBase *meth = match_method (mid, self, args, true);
const gsi::MethodBase *meth = match_method (mid, self, args, kwargs, true);
// method is not implemented
if (! meth) {
@ -703,7 +834,7 @@ method_adaptor (int mid, PyObject *self, PyObject *args)
gsi::SerialArgs retlist (meth->retsize ());
gsi::SerialArgs arglist (meth->argsize ());
push_args (arglist, meth, args, heap);
push_args (arglist, meth, args, kwargs, heap);
meth->call (obj, arglist, retlist);
@ -770,7 +901,7 @@ property_setter_adaptor (int mid, PyObject *self, PyObject *args)
* @brief __init__ implementation (bound to method ith id 'mid')
*/
static PyObject *
method_init_adaptor (int mid, PyObject *self, PyObject *args)
method_init_adaptor (int mid, PyObject *self, PyObject *args, PyObject *kwargs)
{
PYA_TRY
@ -782,7 +913,10 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args)
}
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 ());
bool has_kwargs = kwargs != NULL && PyDict_Size (kwargs) > 0;
bool strict_matching = argc > 0 || has_kwargs || ! p->cls_decl ()->can_default_create ();
const gsi::MethodBase *meth = match_method (mid, self, args, kwargs, strict_matching);
if (meth && meth->smt () == gsi::MethodBase::None) {
@ -791,7 +925,7 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args)
gsi::SerialArgs retlist (meth->retsize ());
gsi::SerialArgs arglist (meth->argsize ());
push_args (arglist, meth, args, heap);
push_args (arglist, meth, args, kwargs, heap);
meth->call (0, arglist, retlist);
@ -979,8 +1113,8 @@ property_setter_impl (int mid, PyObject *self, PyObject *value)
}
if (is_valid) {
++candidates;
meth = *m;
++candidates;
}
}
@ -1075,12 +1209,12 @@ property_setter_func (PyObject *self, PyObject *value, void *closure)
// Adaptor arrays
template <int N>
PyObject *method_adaptor (PyObject *self, PyObject *args)
PyObject *method_adaptor (PyObject *self, PyObject *args, PyObject *kwargs)
{
return method_adaptor (N, self, args);
return method_adaptor (N, self, args, kwargs);
}
static py_func_ptr_t method_adaptors [] =
static py_func_with_kw_ptr_t method_adaptors [] =
{
&method_adaptor<0x000>, &method_adaptor<0x001>, &method_adaptor<0x002>, &method_adaptor<0x003>, &method_adaptor<0x004>, &method_adaptor<0x005>, &method_adaptor<0x006>, &method_adaptor<0x007>,
&method_adaptor<0x008>, &method_adaptor<0x009>, &method_adaptor<0x00a>, &method_adaptor<0x00b>, &method_adaptor<0x00c>, &method_adaptor<0x00d>, &method_adaptor<0x00e>, &method_adaptor<0x00f>,
@ -1244,7 +1378,7 @@ static py_func_ptr_t method_adaptors [] =
&method_adaptor<0x4f8>, &method_adaptor<0x4f9>, &method_adaptor<0x4fa>, &method_adaptor<0x4fb>, &method_adaptor<0x4fc>, &method_adaptor<0x4fd>, &method_adaptor<0x4fe>, &method_adaptor<0x4ff>,
};
py_func_ptr_t get_method_adaptor (int n)
py_func_with_kw_ptr_t get_method_adaptor (int n)
{
tl_assert (n >= 0 && n < int (sizeof (method_adaptors) / sizeof (method_adaptors [0])));
return method_adaptors [n];
@ -1603,12 +1737,12 @@ py_func_ptr_t get_property_setter_adaptor (int n)
}
template <int N>
PyObject *method_init_adaptor (PyObject *self, PyObject *args)
PyObject *method_init_adaptor (PyObject *self, PyObject *args, PyObject *kwargs)
{
return method_init_adaptor (N, self, args);
return method_init_adaptor (N, self, args, kwargs);
}
static py_func_ptr_t method_init_adaptors [] =
static py_func_with_kw_ptr_t method_init_adaptors [] =
{
&method_init_adaptor<0x000>, &method_init_adaptor<0x001>, &method_init_adaptor<0x002>, &method_init_adaptor<0x003>, &method_init_adaptor<0x004>, &method_init_adaptor<0x005>, &method_init_adaptor<0x006>, &method_init_adaptor<0x007>,
&method_init_adaptor<0x008>, &method_init_adaptor<0x009>, &method_init_adaptor<0x00a>, &method_init_adaptor<0x00b>, &method_init_adaptor<0x00c>, &method_init_adaptor<0x00d>, &method_init_adaptor<0x00e>, &method_init_adaptor<0x00f>,
@ -1740,7 +1874,7 @@ static py_func_ptr_t method_init_adaptors [] =
&method_init_adaptor<0x3f8>, &method_init_adaptor<0x3f9>, &method_init_adaptor<0x3fa>, &method_init_adaptor<0x3fb>, &method_init_adaptor<0x3fc>, &method_init_adaptor<0x3fd>, &method_init_adaptor<0x3fe>, &method_init_adaptor<0x3ff>,
};
py_func_ptr_t get_method_init_adaptor (int n)
py_func_with_kw_ptr_t get_method_init_adaptor (int n)
{
tl_assert (n >= 0 && n < int (sizeof (method_init_adaptors) / sizeof (method_init_adaptors [0])));
return method_init_adaptors [n];

View File

@ -39,12 +39,13 @@ PyObject *object_default_le_impl (PyObject *self, PyObject *args);
PyObject *object_default_gt_impl (PyObject *self, PyObject *args);
PyObject *object_default_deepcopy_impl (PyObject *self, PyObject *args);
typedef PyObject *(*py_func_with_kw_ptr_t) (PyObject *, PyObject *, PyObject *);
typedef PyObject *(*py_func_ptr_t) (PyObject *, PyObject *);
py_func_ptr_t get_method_adaptor (int n);
py_func_with_kw_ptr_t get_method_adaptor (int n);
py_func_ptr_t get_property_getter_adaptor (int n);
py_func_ptr_t get_property_setter_adaptor (int n);
py_func_ptr_t get_method_init_adaptor (int n);
py_func_with_kw_ptr_t get_method_init_adaptor (int n);
inline void *make_closure (int mid_getter, int mid_setter)
{

View File

@ -379,7 +379,7 @@ pya_plain_iterator_next (PyObject *self)
gsi::SerialArgs args (iter->iter->serial_size ());
iter->iter->get (args);
PythonRef obj = pop_arg (*iter->value_type, args, 0, heap);
PythonRef obj = pull_arg (*iter->value_type, args, 0, heap);
return obj.release ();
}

View File

@ -32,7 +32,7 @@
namespace pya
{
void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap);
void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, PyObject *kwargs, tl::Heap &heap);
// -------------------------------------------------------------------
// Serialization adaptors for strings, variants, vectors and maps
@ -508,7 +508,7 @@ struct writer<gsi::ObjectType>
gsi::SerialArgs retlist (meth->retsize ());
gsi::SerialArgs arglist (meth->argsize ());
push_args (arglist, meth, arg, *heap);
push_args (arglist, meth, arg, NULL, *heap);
meth->call (0, arglist, retlist);
@ -862,7 +862,7 @@ struct reader<gsi::VoidType>
};
PythonRef
pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap)
pull_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap)
{
PythonRef ret;
gsi::do_on_type<reader> () (atype.type (), &aserial, &ret, self, atype, &heap);

View File

@ -58,7 +58,7 @@ push_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PyObject *arg, tl
* @param self The self object of the method call (for shortcut evaluation to return self if possible)
* @return The deserialized object (a new reference)
*/
PythonRef pop_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap);
PythonRef pull_arg (const gsi::ArgType &atype, gsi::SerialArgs &aserial, PYAObjectBase *self, tl::Heap &heap);
/**
* @brief Tests whether the given object is compatible with the given type

View File

@ -517,12 +517,12 @@ public:
// Special handling needed as the memo argument needs to be ignored
method->ml_meth = &object_default_deepcopy_impl;
} else if (mt->is_init (mid)) {
method->ml_meth = (PyCFunction) get_method_init_adaptor (mid);
method->ml_meth = reinterpret_cast<PyCFunction> (get_method_init_adaptor (mid));
} else {
method->ml_meth = (PyCFunction) get_method_adaptor (mid);
method->ml_meth = reinterpret_cast<PyCFunction> (get_method_adaptor (mid));
}
method->ml_doc = mp_module->make_string (doc);
method->ml_flags = METH_VARARGS;
method->ml_flags = METH_VARARGS | METH_KEYWORDS;
PythonRef attr = PythonRef (PyDescr_NewMethod (type, method));
set_type_attr (type, name, attr);
@ -531,9 +531,9 @@ public:
PyMethodDef *method = mp_module->make_method_def ();
method->ml_name = mp_module->make_string (name);
method->ml_meth = (PyCFunction) get_method_adaptor (mid);
method->ml_meth = reinterpret_cast<PyCFunction> (get_method_adaptor (mid));
method->ml_doc = mp_module->make_string (doc);
method->ml_flags = METH_VARARGS | METH_CLASS;
method->ml_flags = METH_VARARGS | METH_KEYWORDS | METH_CLASS;
PythonRef attr = PythonRef (PyDescr_NewClassMethod (type, method));
set_type_attr (type, name, attr);

View File

@ -145,7 +145,7 @@ Callee::call (int id, gsi::SerialArgs &args, gsi::SerialArgs &ret) const
// TODO: callbacks with default arguments?
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) {
PyTuple_SetItem (argv.get (), arg4self + (a - meth->begin_arguments ()), pop_arg (*a, args, 0, heap).release ());
PyTuple_SetItem (argv.get (), arg4self + (a - meth->begin_arguments ()), pull_arg (*a, args, 0, heap).release ());
}
PythonRef result (PyObject_CallObject (callable.get (), argv.get ()));

View File

@ -130,7 +130,7 @@ void SignalHandler::call (const gsi::MethodBase *meth, gsi::SerialArgs &args, gs
int args_avail = int (std::distance (meth->begin_arguments (), meth->end_arguments ()));
PythonRef argv (PyTuple_New (args_avail));
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); args && a != meth->end_arguments (); ++a) {
PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pop_arg (*a, args, 0, heap).release ());
PyTuple_SetItem (argv.get (), int (a - meth->begin_arguments ()), pull_arg (*a, args, 0, heap).release ());
}
// NOTE: in case one event handler deletes the object, it's safer to first collect the handlers and

View File

@ -96,6 +96,7 @@ void run_pythontest (tl::TestBase *_this, const std::string &fn)
#define PYTHONTEST(n, file) \
TEST(n) { run_pythontest(_this, file); }
PYTHONTEST (kwargs, "kwargs.py")
PYTHONTEST (dbLayoutTest, "dbLayoutTest.py")
PYTHONTEST (dbRegionTest, "dbRegionTest.py")
PYTHONTEST (dbReaders, "dbReaders.py")

209
testdata/python/kwargs.py vendored Normal file
View File

@ -0,0 +1,209 @@
# KLayout Layout Viewer
# Copyright (C) 2006-2023 Matthias Koefferlein
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import pya
import unittest
# NOTE: pya.CplxTrans and pya.Trans and good test cases
# for the keyword arguments feature
class KWArgsTest(unittest.TestCase):
def test_1(self):
t = pya.CplxTrans()
self.assertEqual(str(t), "r0 *1 0,0")
t = pya.CplxTrans(1.5)
self.assertEqual(str(t), "r0 *1.5 0,0")
t = pya.CplxTrans(1, 2)
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.CplxTrans(1, y = 2)
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.CplxTrans(x = 1, y = 2)
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.CplxTrans(u = pya.DVector(1, 2))
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.CplxTrans(pya.DVector(1, 2))
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.CplxTrans(u = pya.Vector(1, 2))
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.CplxTrans(u = (1, 2))
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.CplxTrans(mag = 1.5)
self.assertEqual(str(t), "r0 *1.5 0,0")
t = pya.CplxTrans(1.5, 45, True, 1, 2)
self.assertEqual(str(t), "m22.5 *1.5 1,2")
t = pya.CplxTrans(1.5, 45, True, pya.DVector(1, 2))
self.assertEqual(str(t), "m22.5 *1.5 1,2")
t = pya.CplxTrans(1.5, x = 1, y = 2, mirrx = True, rot = 45)
self.assertEqual(str(t), "m22.5 *1.5 1,2")
t = pya.CplxTrans(pya.CplxTrans.M0)
self.assertEqual(str(t), "m0 *1 0,0")
t = pya.CplxTrans(pya.CplxTrans.M0, u = pya.DVector(1, 2))
self.assertEqual(str(t), "m0 *1 1,2")
t = pya.CplxTrans(pya.CplxTrans.M0, mag = 1.5, u = pya.DVector(1, 2))
self.assertEqual(str(t), "m0 *1.5 1,2")
t = pya.CplxTrans(pya.CplxTrans.M0, 1.5, pya.DVector(1, 2))
self.assertEqual(str(t), "m0 *1.5 1,2")
t = pya.CplxTrans(pya.CplxTrans.M0, mag = 1.5, x = 1, y = 2)
self.assertEqual(str(t), "m0 *1.5 1,2")
t = pya.CplxTrans(pya.CplxTrans.M0, 1.5, 1, 2)
self.assertEqual(str(t), "m0 *1.5 1,2")
t = pya.CplxTrans(pya.VCplxTrans.M0)
self.assertEqual(str(t), "m0 *1 0,0")
t = pya.CplxTrans(pya.ICplxTrans.M0)
self.assertEqual(str(t), "m0 *1 0,0")
t = pya.CplxTrans(pya.DCplxTrans.M0)
self.assertEqual(str(t), "m0 *1 0,0")
t = pya.CplxTrans(pya.Trans.M0)
self.assertEqual(str(t), "m0 *1 0,0")
t = pya.CplxTrans(pya.Trans.M0, 1.5)
self.assertEqual(str(t), "m0 *1.5 0,0")
t = pya.CplxTrans(pya.Trans.M0, mag = 1.5)
self.assertEqual(str(t), "m0 *1.5 0,0")
t = pya.CplxTrans(t = pya.Trans.M0, mag = 1.5)
self.assertEqual(str(t), "m0 *1.5 0,0")
t = pya.CplxTrans()
t.disp = (1, 2)
self.assertEqual(str(t), "r0 *1 1,2")
t = pya.ICplxTrans(15, 25)
self.assertEqual(t.to_s(dbu = 0.01), "r0 *1 0.15000,0.25000")
def test_2(self):
t = pya.Trans(pya.Trans.M0, 1, 2)
self.assertEqual(str(t), "m0 1,2")
t = pya.Trans(pya.Trans.M0, x = 1, y = 2)
self.assertEqual(str(t), "m0 1,2")
t = pya.Trans(pya.Trans.M0, pya.Vector(1, 2))
self.assertEqual(str(t), "m0 1,2")
t = pya.Trans(pya.Trans.M0, u = pya.Vector(1, 2))
self.assertEqual(str(t), "m0 1,2")
t = pya.Trans(rot = 3, mirrx = True)
self.assertEqual(str(t), "m135 0,0")
t = pya.Trans(rot = 3, mirrx = True, x = 1, y = 2)
self.assertEqual(str(t), "m135 1,2")
t = pya.Trans(3, True, 1, 2)
self.assertEqual(str(t), "m135 1,2")
t = pya.Trans(3, True, pya.Vector(1, 2))
self.assertEqual(str(t), "m135 1,2")
t = pya.Trans(rot = 3, mirrx = True, u = pya.Vector(1, 2))
self.assertEqual(str(t), "m135 1,2")
t = pya.Trans()
self.assertEqual(str(t), "r0 0,0")
t = pya.Trans(pya.DTrans.M0)
self.assertEqual(str(t), "m0 0,0")
t = pya.Trans(pya.DTrans.M0, 1, 2)
self.assertEqual(str(t), "m0 1,2")
t = pya.Trans(pya.DTrans.M0, x = 1, y = 2)
self.assertEqual(str(t), "m0 1,2")
t = pya.Trans(c = pya.DTrans.M0, x = 1, y = 2)
self.assertEqual(str(t), "m0 1,2")
t = pya.Trans(pya.Vector(1, 2))
self.assertEqual(str(t), "r0 1,2")
t = pya.Trans(1, 2)
self.assertEqual(str(t), "r0 1,2")
def test_3(self):
try:
t = pya.CplxTrans(1.5, 2.5)
t.to_s(dbu = "17")
self.assertEqual(True, False)
except Exception as ex:
self.assertEqual(str(ex), "Value cannot be converted to a floating-point value for argument #2 ('dbu') in CplxTrans.to_s")
try:
t = pya.CplxTrans("17")
self.assertEqual(True, False)
except Exception as ex:
self.assertEqual(str(ex).find("No overload with matching arguments."), 0)
try:
t = pya.CplxTrans(uu = 17)
self.assertEqual(True, False)
except Exception as ex:
self.assertEqual(str(ex).find("Can't match arguments."), 0)
try:
t = pya.CplxTrans(u = "17")
self.assertEqual(True, False)
except Exception as ex:
self.assertEqual(str(ex).find("No overload with matching arguments."), 0)
try:
t = pya.Trans("17")
self.assertEqual(True, False)
except Exception as ex:
self.assertEqual(str(ex).find("No overload with matching arguments."), 0)
# run unit tests
if __name__ == '__main__':
suite = unittest.TestSuite()
# NOTE: Use this instead of loadTestsfromTestCase to select a specific test:
# suite.addTest(KWArgsTest("test_26"))
suite = unittest.TestLoader().loadTestsFromTestCase(KWArgsTest)
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
sys.exit(1)