mirror of
https://github.com/KLayout/klayout.git
synced 2026-09-06 17:33:25 +02:00
603 lines
17 KiB
C++
603 lines
17 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 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 "pyaConvert.h"
|
|
#include "pyaObject.h"
|
|
|
|
#include "pya.h"
|
|
#include "pyaUtils.h"
|
|
|
|
#include <string>
|
|
|
|
namespace pya
|
|
{
|
|
|
|
template <>
|
|
long python2c<long> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyInt_Check (rval)) {
|
|
return PyInt_AsLong (rval);
|
|
} else
|
|
#endif
|
|
if (PyLong_Check (rval)) {
|
|
return PyLong_AsLong (rval);
|
|
} else if (PyFloat_Check (rval)) {
|
|
return (long) (PyFloat_AsDouble (rval));
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to an integer")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
bool python2c<bool> (PyObject *rval, tl::Heap *)
|
|
{
|
|
return PyObject_IsTrue (rval);
|
|
}
|
|
|
|
template <>
|
|
char python2c<char> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyInt_Check (rval)) {
|
|
return char (PyInt_AsLong (rval));
|
|
} else
|
|
#endif
|
|
if (PyLong_Check (rval)) {
|
|
return char (PyLong_AsLong (rval));
|
|
} else if (PyFloat_Check (rval)) {
|
|
return char (PyFloat_AsDouble (rval));
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to a character")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
unsigned long python2c<unsigned long> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyInt_Check (rval)) {
|
|
return PyInt_AsUnsignedLongMask (rval);
|
|
} else
|
|
#endif
|
|
if (PyLong_Check (rval)) {
|
|
return PyLong_AsUnsignedLongMask (rval);
|
|
} else if (PyFloat_Check (rval)) {
|
|
return (unsigned long) (PyFloat_AsDouble (rval));
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to an integer")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
long long python2c<long long> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyInt_Check (rval)) {
|
|
return PyInt_AsLong (rval);
|
|
} else
|
|
#endif
|
|
if (PyLong_Check (rval)) {
|
|
return PyLong_AsLongLong (rval);
|
|
} else if (PyFloat_Check (rval)) {
|
|
return (long long) (PyFloat_AsDouble (rval));
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to an integer")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
unsigned long long python2c<unsigned long long> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyInt_Check (rval)) {
|
|
return PyInt_AsUnsignedLongMask (rval);
|
|
} else
|
|
#endif
|
|
if (PyLong_Check (rval)) {
|
|
return PyLong_AsUnsignedLongLongMask (rval);
|
|
} else if (PyFloat_Check (rval)) {
|
|
return (unsigned long long) (PyFloat_AsDouble (rval));
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to an integer")));
|
|
}
|
|
}
|
|
|
|
#if defined(HAVE_64BIT_COORD)
|
|
template <>
|
|
__int128 python2c<__int128> (PyObject *rval, tl::Heap *)
|
|
{
|
|
// TOOD: this is pretty simplistic
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyInt_Check (rval)) {
|
|
return PyInt_AsLong (rval);
|
|
} else
|
|
#endif
|
|
if (PyLong_Check (rval)) {
|
|
return PyLong_AsLongLong (rval);
|
|
} else if (PyFloat_Check (rval)) {
|
|
return PyFloat_AsDouble (rval);
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to an integer")));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
template <>
|
|
double python2c<double> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyInt_Check (rval)) {
|
|
return PyInt_AsLong (rval);
|
|
} else
|
|
#endif
|
|
if (PyLong_Check (rval)) {
|
|
return PyLong_AsLongLong (rval);
|
|
} else if (PyFloat_Check (rval)) {
|
|
return PyFloat_AsDouble (rval);
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to a floating-point value")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
std::string python2c<std::string> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyString_Check (rval)) {
|
|
return std::string (PyString_AsString (rval), PyString_Size (rval));
|
|
} else
|
|
#else
|
|
if (PyBytes_Check (rval)) {
|
|
return std::string (PyBytes_AsString (rval), PyBytes_Size (rval));
|
|
} else
|
|
#endif
|
|
if (PyUnicode_Check (rval)) {
|
|
PythonRef ba (PyUnicode_AsUTF8String (rval));
|
|
if (! ba) {
|
|
check_error ();
|
|
}
|
|
return std::string (PyBytes_AsString (ba.get ()), PyBytes_Size (ba.get ()));
|
|
} else if (PyByteArray_Check (rval)) {
|
|
return std::string (PyByteArray_AsString (rval), PyByteArray_Size (rval));
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to a string")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
QByteArray python2c<QByteArray> (PyObject *rval, tl::Heap *)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyString_Check (rval)) {
|
|
return QByteArray (PyString_AsString (rval), PyString_Size (rval));
|
|
} else
|
|
#else
|
|
if (PyBytes_Check (rval)) {
|
|
return QByteArray (PyBytes_AsString (rval), PyBytes_Size (rval));
|
|
} else
|
|
#endif
|
|
if (PyUnicode_Check (rval)) {
|
|
PythonRef ba (PyUnicode_AsUTF8String (rval));
|
|
if (! ba) {
|
|
check_error ();
|
|
}
|
|
return QByteArray (PyBytes_AsString (ba.get ()), PyBytes_Size (ba.get ()));
|
|
} else if (PyByteArray_Check (rval)) {
|
|
return QByteArray (PyByteArray_AsString (rval), PyByteArray_Size (rval));
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to a byte array")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
const char *python2c<const char *> (PyObject *rval, tl::Heap *heap)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
if (PyString_Check (rval)) {
|
|
return PyString_AsString (rval);
|
|
} else
|
|
#else
|
|
if (PyBytes_Check (rval)) {
|
|
return PyBytes_AsString (rval);
|
|
} else
|
|
#endif
|
|
if (PyUnicode_Check (rval)) {
|
|
|
|
tl_assert (heap);
|
|
|
|
// for creating a const char * we need a temporary object. Otherwise we cannot keep that
|
|
// value.
|
|
std::string *converted_value = new std::string (python2c<std::string> (rval, heap));
|
|
heap->push (converted_value);
|
|
|
|
return converted_value->c_str ();
|
|
|
|
} else if (PyByteArray_Check (rval)) {
|
|
return PyByteArray_AsString (rval);
|
|
} else {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Argument cannot be converted to a string")));
|
|
}
|
|
}
|
|
|
|
template <>
|
|
QString python2c<QString> (PyObject *rval, tl::Heap *heap)
|
|
{
|
|
// TODO: directly convert Unicode strings to QString if possible
|
|
return tl::to_qstring (python2c<std::string> (rval, heap));
|
|
}
|
|
|
|
template <>
|
|
tl::Variant python2c<tl::Variant> (PyObject *rval, tl::Heap *heap)
|
|
{
|
|
if (rval == NULL || rval == Py_None) {
|
|
return tl::Variant ();
|
|
} else if (PyBool_Check (rval)) {
|
|
return tl::Variant (python2c<bool> (rval, heap));
|
|
} else if (PyLong_Check (rval)) {
|
|
return tl::Variant (python2c<long long> (rval, heap));
|
|
#if PY_MAJOR_VERSION < 3
|
|
} else if (PyInt_Check (rval)) {
|
|
return tl::Variant (python2c<int> (rval, heap));
|
|
#endif
|
|
} else if (PyFloat_Check (rval)) {
|
|
return tl::Variant (python2c<double> (rval, heap));
|
|
#if PY_MAJOR_VERSION < 3
|
|
} else if (PyString_Check (rval)) {
|
|
return tl::Variant (python2c<std::string> (rval, heap));
|
|
#else
|
|
} else if (PyBytes_Check (rval)) {
|
|
return tl::Variant (python2c<std::string> (rval, heap));
|
|
#endif
|
|
} else if (PyUnicode_Check (rval) || PyByteArray_Check (rval)) {
|
|
return tl::Variant (python2c<std::string> (rval, heap));
|
|
} else if (PyList_Check (rval)) {
|
|
|
|
size_t len = PyList_Size (rval);
|
|
|
|
static std::vector<tl::Variant> empty;
|
|
tl::Variant r (empty.begin (), empty.end ());
|
|
r.get_list ().reserve (len);
|
|
for (size_t i = 0; i < len; ++i) {
|
|
r.get_list ().push_back (python2c<tl::Variant> (PyList_GetItem (rval, i), heap));
|
|
}
|
|
return r;
|
|
|
|
} else if (PyTuple_Check (rval)) {
|
|
|
|
size_t len = PyTuple_Size (rval);
|
|
|
|
static std::vector<tl::Variant> empty;
|
|
tl::Variant r (empty.begin (), empty.end ());
|
|
r.get_list ().reserve (len);
|
|
for (size_t i = 0; i < len; ++i) {
|
|
r.get_list ().push_back (python2c<tl::Variant> (PyTuple_GetItem (rval, i), heap));
|
|
}
|
|
return r;
|
|
|
|
} else if (PyDict_Check (rval)) {
|
|
|
|
tl::Variant r;
|
|
r.set_array ();
|
|
Py_ssize_t pos = 0;
|
|
PyObject *key, *value;
|
|
while (PyDict_Next (rval, &pos, &key, &value)) {
|
|
r.insert (python2c<tl::Variant> (key), python2c<tl::Variant> (value));
|
|
}
|
|
|
|
return r;
|
|
|
|
} else {
|
|
|
|
const gsi::ClassBase *cls = PythonInterpreter::instance ()->cls_for_type (Py_TYPE (rval));
|
|
if (cls) {
|
|
|
|
PYAObjectBase *p = (PYAObjectBase *) rval;
|
|
|
|
// employ the tl::Variant binding capabilities of the Expression binding to derive the
|
|
// variant value.
|
|
|
|
void *obj = p->obj ();
|
|
if (! obj) {
|
|
return tl::Variant ();
|
|
}
|
|
|
|
if (cls->is_managed ()) {
|
|
|
|
const tl::VariantUserClassBase *var_cls = cls->var_cls (p->const_ref ());
|
|
tl_assert (var_cls != 0);
|
|
|
|
gsi::Proxy *gsi_proxy = cls->gsi_object (obj)->find_client<gsi::Proxy> ();
|
|
if (!gsi_proxy) {
|
|
// establish a new proxy
|
|
gsi_proxy = new gsi::Proxy (cls);
|
|
gsi_proxy->set (obj, false, p->const_ref (), false);
|
|
}
|
|
|
|
tl::Variant out;
|
|
out.set_user_ref (gsi_proxy, var_cls, false);
|
|
return out;
|
|
|
|
} else {
|
|
// No reference management available: deep copy mode.
|
|
return tl::Variant (cls->clone (obj), cls->var_cls (false), true);
|
|
}
|
|
|
|
} else {
|
|
|
|
// last resort: convert to string
|
|
std::string m ("<unknown type>");
|
|
PythonRef msg_str (PyObject_Str (rval));
|
|
if (msg_str) {
|
|
m = python2c<std::string> (msg_str.get (), heap);
|
|
}
|
|
return tl::Variant (m);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
PyObject *
|
|
object_to_python (void *obj, const gsi::ArgType &atype)
|
|
{
|
|
const gsi::ClassBase *cls = atype.cls()->subclass_decl (obj);
|
|
|
|
bool is_direct = !(atype.is_ptr () || atype.is_ref () || atype.is_cptr () || atype.is_cref ());
|
|
bool pass_obj = atype.pass_obj () || is_direct;
|
|
bool is_const = atype.is_cptr () || atype.is_cref ();
|
|
bool prefer_copy = atype.is_cref ();
|
|
bool can_destroy = prefer_copy || atype.is_ptr ();
|
|
|
|
return object_to_python (obj, cls, pass_obj, is_const, prefer_copy, can_destroy);
|
|
}
|
|
|
|
/**
|
|
* @brief Correct constness if a reference is const and a non-const reference is required
|
|
* HINT: this is a workaround for the fact that unlike C++, Python does not have const or non-const
|
|
* references. Since a reference is identical with the object it points to, there are only const or non-const
|
|
* objects. We deliver const objects first, but if a non-const version is requestes, the
|
|
* object turns into a non-const one. This may be confusing but provides a certain level
|
|
* of "constness", at least until there is another non-const reference for that object.
|
|
*/
|
|
void correct_constness (PyObject *obj, bool const_required)
|
|
{
|
|
PYAObjectBase *p = (PYAObjectBase *) obj;
|
|
if (p->const_ref () && ! const_required) {
|
|
// promote to non-const object
|
|
p->set_const_ref (false);
|
|
}
|
|
}
|
|
|
|
PyObject *
|
|
object_to_python (void *obj, const gsi::ClassBase *cls, bool pass_obj, bool is_const, bool prefer_copy, bool can_destroy)
|
|
{
|
|
if (! obj || ! cls) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
const gsi::ClassBase *clsact = cls->subclass_decl (obj);
|
|
if (! clsact) {
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
PYAObjectBase *pya_object = 0;
|
|
if (! clsact->adapted_type_info () && clsact->is_managed ()) {
|
|
|
|
StatusChangedListener *client = clsact->gsi_object (obj)->find_client<StatusChangedListener> ();
|
|
if (client) {
|
|
pya_object = client->pya_object();
|
|
}
|
|
|
|
} else if (clsact->adapted_type_info ()) {
|
|
|
|
// create an adaptor from an adapted type
|
|
if (pass_obj) {
|
|
obj = clsact->create_from_adapted_consume (obj);
|
|
} else {
|
|
obj = clsact->create_from_adapted (obj);
|
|
}
|
|
|
|
// we wil own the new object
|
|
pass_obj = true;
|
|
|
|
}
|
|
|
|
if (! pass_obj && prefer_copy && ! clsact->adapted_type_info () && ! clsact->is_managed () && clsact->can_copy ()) {
|
|
|
|
// We copy objects passed by const reference if they are not managed
|
|
// (derived from gsi::ObjectBase, in that case, client_data is not 0).
|
|
// Such objects are often exposed internals. First we can't
|
|
// guarantee the const correctness of references. Second, we
|
|
// can't guarantee the lifetime of the container will exceed that
|
|
// of the exposed property. Hence copying is safer.
|
|
|
|
PyTypeObject *type = PythonInterpreter::instance ()->type_for_cls (clsact);
|
|
tl_assert (type != NULL);
|
|
|
|
// create a instance and copy the value
|
|
PYAObjectBase *new_object = (PYAObjectBase *) type->tp_alloc (type, 0);
|
|
new (new_object) PYAObjectBase (clsact);
|
|
clsact->assign (new_object->obj (), obj);
|
|
return new_object;
|
|
|
|
} else if (pya_object) {
|
|
|
|
// we have a that is located in C++ space but is supposed to get attached
|
|
// a Python object. If it already has, we simply return a reference to this
|
|
Py_INCREF (pya_object);
|
|
|
|
correct_constness (pya_object, is_const);
|
|
|
|
return pya_object;
|
|
|
|
} else {
|
|
|
|
PyTypeObject *type = PythonInterpreter::instance ()->type_for_cls (clsact);
|
|
tl_assert (type != NULL);
|
|
|
|
// create a instance and copy the value
|
|
PYAObjectBase *new_object = (PYAObjectBase *) type->tp_alloc (type, 0);
|
|
new (new_object) PYAObjectBase (clsact);
|
|
new_object->set (obj, pass_obj, is_const, can_destroy);
|
|
return new_object;
|
|
|
|
}
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<tl::Variant> (const tl::Variant &c)
|
|
{
|
|
if (c.is_double ()) {
|
|
return c2python<double> (c.to_double ());
|
|
} else if (c.is_bool ()) {
|
|
return c2python<bool> (c.to_bool ());
|
|
} else if (c.is_a_string ()) {
|
|
return c2python<std::string> (c.to_string ());
|
|
} else if (c.is_long ()) {
|
|
return c2python<long> (c.to_long ());
|
|
} else if (c.is_ulong ()) {
|
|
return c2python<unsigned long> (c.to_ulong ());
|
|
} else if (c.is_longlong ()) {
|
|
return c2python<long long> (c.to_longlong ());
|
|
} else if (c.is_ulonglong ()) {
|
|
return c2python<unsigned long long> (c.to_ulonglong ());
|
|
} else if (c.is_array ()) {
|
|
|
|
PyObject *ret = PyDict_New ();
|
|
for (tl::Variant::const_array_iterator i = c.begin_array (); i != c.end_array (); ++i) {
|
|
PyDict_SetItem (ret, c2python<tl::Variant> (i->first), c2python<tl::Variant> (i->second));
|
|
}
|
|
return ret;
|
|
|
|
} else if (c.is_list ()) {
|
|
|
|
PyObject *ret = PyList_New (c.get_list ().size ());
|
|
size_t index = 0;
|
|
for (tl::Variant::const_iterator i = c.begin (); i != c.end (); ++i, ++index) {
|
|
PyList_SetItem (ret, index, c2python<tl::Variant> (*i));
|
|
}
|
|
return ret;
|
|
|
|
} else if (c.is_user ()) {
|
|
|
|
const gsi::ClassBase *cls = c.gsi_cls ();
|
|
if (cls) {
|
|
void *obj = const_cast<void *> (c.to_user ());
|
|
return object_to_python (obj, c.user_cls ()->gsi_cls (), false, false, true, false);
|
|
} else {
|
|
// not a known type -> return nil
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
} else {
|
|
Py_RETURN_NONE;
|
|
}
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<gsi::ObjectType> (const gsi::ObjectType &)
|
|
{
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Internal error: abstract object type read")));
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<gsi::VectorType> (const gsi::VectorType &)
|
|
{
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Internal error: abstract vector type read")));
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<gsi::MapType> (const gsi::MapType &)
|
|
{
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Internal error: abstract map type read")));
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<gsi::VoidType> (const gsi::VoidType &)
|
|
{
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Internal error: abstract void type read")));
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<std::string> (const std::string &c)
|
|
{
|
|
#if PY_MAJOR_VERSION < 3
|
|
return PyString_FromStringAndSize (c.c_str (), Py_ssize_t (c.size ()));
|
|
#else
|
|
PyObject *ret = PyUnicode_DecodeUTF8 (c.c_str (), Py_ssize_t (c.size ()), NULL);
|
|
if (ret == NULL) {
|
|
check_error ();
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<const char *> (const char * const &p)
|
|
{
|
|
const char *s = p;
|
|
if (! s) {
|
|
s = "(null)";
|
|
}
|
|
|
|
#if PY_MAJOR_VERSION < 3
|
|
return PyString_FromString (s);
|
|
#else
|
|
PyObject *ret = PyUnicode_DecodeUTF8 (p, strlen (p), NULL);
|
|
if (ret == NULL) {
|
|
check_error ();
|
|
}
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<QByteArray> (const QByteArray &qba)
|
|
{
|
|
if (qba.isNull ()) {
|
|
Py_RETURN_NONE;
|
|
} else {
|
|
#if PY_MAJOR_VERSION < 3
|
|
return PyString_FromStringAndSize (qba.constData (), Py_ssize_t (qba.size ()));
|
|
#else
|
|
return PyByteArray_FromStringAndSize (qba.constData (), Py_ssize_t (qba.size ()));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
template <>
|
|
PyObject *c2python<QString> (const QString &qs)
|
|
{
|
|
if (qs.isNull ()) {
|
|
Py_RETURN_NONE;
|
|
} else {
|
|
// TODO: can be done more efficently
|
|
std::string c (tl::to_string (qs));
|
|
return c2python<std::string> (c);
|
|
}
|
|
}
|
|
|
|
}
|
|
|