diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-06-25 22:59:01 +0000 |
---|---|---|
committer | <> | 2013-09-27 11:49:28 +0000 |
commit | 8c4528713d907ee2cfd3bfcbbad272c749867f84 (patch) | |
tree | c09e2ce80f47b90c85cc720f5139089ad9c8cfff /libs/python/src/object | |
download | boost-tarball-baserock/morph.tar.gz |
Imported from /home/lorry/working-area/delta_boost-tarball/boost_1_54_0.tar.bz2.boost_1_54_0baserock/morph
Diffstat (limited to 'libs/python/src/object')
-rw-r--r-- | libs/python/src/object/class.cpp | 764 | ||||
-rw-r--r-- | libs/python/src/object/enum.cpp | 246 | ||||
-rw-r--r-- | libs/python/src/object/function.cpp | 793 | ||||
-rw-r--r-- | libs/python/src/object/function_doc_signature.cpp | 344 | ||||
-rw-r--r-- | libs/python/src/object/inheritance.cpp | 495 | ||||
-rw-r--r-- | libs/python/src/object/iterator.cpp | 39 | ||||
-rw-r--r-- | libs/python/src/object/life_support.cpp | 121 | ||||
-rw-r--r-- | libs/python/src/object/pickle_support.cpp | 78 | ||||
-rw-r--r-- | libs/python/src/object/stl_iterator.cpp | 48 |
9 files changed, 2928 insertions, 0 deletions
diff --git a/libs/python/src/object/class.cpp b/libs/python/src/object/class.cpp new file mode 100644 index 000000000..aeef688e2 --- /dev/null +++ b/libs/python/src/object/class.cpp @@ -0,0 +1,764 @@ +// Copyright David Abrahams 2001. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/python/detail/prefix.hpp> +#include <boost/mpl/lambda.hpp> // #including this first is an intel6 workaround + +#include <boost/python/object/class.hpp> +#include <boost/python/object/instance.hpp> +#include <boost/python/object/class_detail.hpp> +#include <boost/python/scope.hpp> +#include <boost/python/converter/registry.hpp> +#include <boost/python/object/find_instance.hpp> +#include <boost/python/object/pickle_support.hpp> +#include <boost/python/detail/map_entry.hpp> +#include <boost/python/object.hpp> +#include <boost/python/object_protocol.hpp> +#include <boost/detail/binary_search.hpp> +#include <boost/python/self.hpp> +#include <boost/python/dict.hpp> +#include <boost/python/str.hpp> +#include <boost/python/ssize_t.hpp> +#include <functional> +#include <vector> +#include <cstddef> +#include <new> +#include <structmember.h> + +namespace boost { namespace python { + +# ifdef BOOST_PYTHON_SELF_IS_CLASS +namespace self_ns +{ + self_t self; +} +# endif + +instance_holder::instance_holder() + : m_next(0) +{ +} + +instance_holder::~instance_holder() +{ +} + +extern "C" +{ + // This is copied from typeobject.c in the Python sources. Even though + // class_metatype_object doesn't set Py_TPFLAGS_HAVE_GC, that bit gets + // filled in by the base class initialization process in + // PyType_Ready(). However, tp_is_gc is *not* copied from the base + // type, making it assume that classes are GC-able even if (like + // class_type_object) they're statically allocated. + static int + type_is_gc(PyTypeObject *python_type) + { + return python_type->tp_flags & Py_TPFLAGS_HEAPTYPE; + } + + // This is also copied from the Python sources. We can't implement + // static_data as a subclass property effectively without it. + typedef struct { + PyObject_HEAD + PyObject *prop_get; + PyObject *prop_set; + PyObject *prop_del; + PyObject *prop_doc; + int getter_doc; + } propertyobject; + + // Copied from Python source and removed the part for setting docstring, + // since we don't have a setter for __doc__ and trying to set it will + // cause the init fail. + static int property_init(PyObject *self, PyObject *args, PyObject *kwds) + { + PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL; + static const char *kwlist[] = {"fget", "fset", "fdel", "doc", 0}; + propertyobject *prop = (propertyobject *)self; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property", + const_cast<char **>(kwlist), &get, &set, &del, &doc)) + return -1; + + if (get == Py_None) + get = NULL; + if (set == Py_None) + set = NULL; + if (del == Py_None) + del = NULL; + + Py_XINCREF(get); + Py_XINCREF(set); + Py_XINCREF(del); + Py_XINCREF(doc); + + prop->prop_get = get; + prop->prop_set = set; + prop->prop_del = del; + prop->prop_doc = doc; + prop->getter_doc = 0; + + return 0; + } + + + static PyObject * + static_data_descr_get(PyObject *self, PyObject * /*obj*/, PyObject * /*type*/) + { + propertyobject *gs = (propertyobject *)self; + + return PyObject_CallFunction(gs->prop_get, const_cast<char*>("()")); + } + + static int + static_data_descr_set(PyObject *self, PyObject * /*obj*/, PyObject *value) + { + propertyobject *gs = (propertyobject *)self; + PyObject *func, *res; + + if (value == NULL) + func = gs->prop_del; + else + func = gs->prop_set; + if (func == NULL) { + PyErr_SetString(PyExc_AttributeError, + value == NULL ? + "can't delete attribute" : + "can't set attribute"); + return -1; + } + if (value == NULL) + res = PyObject_CallFunction(func, const_cast<char*>("()")); + else + res = PyObject_CallFunction(func, const_cast<char*>("(O)"), value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; + } +} + +static PyTypeObject static_data_object = { + PyVarObject_HEAD_INIT(NULL, 0) + const_cast<char*>("Boost.Python.StaticProperty"), + sizeof(propertyobject), + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT // | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, //&PyProperty_Type, /* tp_base */ + 0, /* tp_dict */ + static_data_descr_get, /* tp_descr_get */ + static_data_descr_set, /* tp_descr_set */ + 0, /* tp_dictoffset */ + property_init, /* tp_init */ + 0, /* tp_alloc */ + 0, // filled in with type_new /* tp_new */ + 0, // filled in with __PyObject_GC_Del /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#if PYTHON_API_VERSION >= 1012 + 0 /* tp_del */ +#endif +}; + +namespace objects +{ +#if PY_VERSION_HEX < 0x03000000 + // XXX Not sure why this run into compiling error in Python 3 + extern "C" + { + // This declaration needed due to broken Python 2.2 headers + extern DL_IMPORT(PyTypeObject) PyProperty_Type; + } +#endif + + BOOST_PYTHON_DECL PyObject* static_data() + { + if (static_data_object.tp_dict == 0) + { + Py_TYPE(&static_data_object) = &PyType_Type; + static_data_object.tp_base = &PyProperty_Type; + if (PyType_Ready(&static_data_object)) + return 0; + } + return upcast<PyObject>(&static_data_object); + } +} + +extern "C" +{ + // Ordinarily, descriptors have a certain assymetry: you can use + // them to read attributes off the class object they adorn, but + // writing the same attribute on the class object always replaces + // the descriptor in the class __dict__. In order to properly + // represent C++ static data members, we need to allow them to be + // written through the class instance. This function of the + // metaclass makes it possible. + static int + class_setattro(PyObject *obj, PyObject *name, PyObject* value) + { + // Must use "private" Python implementation detail + // _PyType_Lookup instead of PyObject_GetAttr because the + // latter will always end up calling the descr_get function on + // any descriptor it finds; we need the unadulterated + // descriptor here. + PyObject* a = _PyType_Lookup(downcast<PyTypeObject>(obj), name); + + // a is a borrowed reference or 0 + + // If we found a static data descriptor, call it directly to + // force it to set the static data member + if (a != 0 && PyObject_IsInstance(a, objects::static_data())) + return Py_TYPE(a)->tp_descr_set(a, obj, value); + else + return PyType_Type.tp_setattro(obj, name, value); + } +} + +static PyTypeObject class_metatype_object = { + PyVarObject_HEAD_INIT(NULL, 0) + const_cast<char*>("Boost.Python.class"), + PyType_Type.tp_basicsize, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + class_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT // | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, //&PyType_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, // filled in with type_new /* tp_new */ + 0, // filled in with __PyObject_GC_Del /* tp_free */ + (inquiry)type_is_gc, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#if PYTHON_API_VERSION >= 1012 + 0 /* tp_del */ +#endif +}; + +// Install the instance data for a C++ object into a Python instance +// object. +void instance_holder::install(PyObject* self) throw() +{ + assert(PyType_IsSubtype(Py_TYPE(Py_TYPE(self)), &class_metatype_object)); + m_next = ((objects::instance<>*)self)->objects; + ((objects::instance<>*)self)->objects = this; +} + + +namespace objects +{ +// Get the metatype object for all extension classes. + BOOST_PYTHON_DECL type_handle class_metatype() + { + if (class_metatype_object.tp_dict == 0) + { + Py_TYPE(&class_metatype_object) = &PyType_Type; + class_metatype_object.tp_base = &PyType_Type; + if (PyType_Ready(&class_metatype_object)) + return type_handle(); + } + return type_handle(borrowed(&class_metatype_object)); + } + extern "C" + { + static void instance_dealloc(PyObject* inst) + { + instance<>* kill_me = (instance<>*)inst; + + for (instance_holder* p = kill_me->objects, *next; p != 0; p = next) + { + next = p->next(); + p->~instance_holder(); + instance_holder::deallocate(inst, dynamic_cast<void*>(p)); + } + + // Python 2.2.1 won't add weak references automatically when + // tp_itemsize > 0, so we need to manage that + // ourselves. Accordingly, we also have to clean up the + // weakrefs ourselves. + if (kill_me->weakrefs != NULL) + PyObject_ClearWeakRefs(inst); + + Py_XDECREF(kill_me->dict); + + Py_TYPE(inst)->tp_free(inst); + } + + static PyObject * + instance_new(PyTypeObject* type_, PyObject* /*args*/, PyObject* /*kw*/) + { + // Attempt to find the __instance_size__ attribute. If not present, no problem. + PyObject* d = type_->tp_dict; + PyObject* instance_size_obj = PyObject_GetAttrString(d, const_cast<char*>("__instance_size__")); + + ssize_t instance_size = instance_size_obj ? +#if PY_VERSION_HEX >= 0x03000000 + PyLong_AsSsize_t(instance_size_obj) : 0; +#else + PyInt_AsLong(instance_size_obj) : 0; +#endif + + if (instance_size < 0) + instance_size = 0; + + PyErr_Clear(); // Clear any errors that may have occurred. + + instance<>* result = (instance<>*)type_->tp_alloc(type_, instance_size); + if (result) + { + // Guido says we can use ob_size for any purpose we + // like, so we'll store the total size of the object + // there. A negative number indicates that the extra + // instance memory is not yet allocated to any holders. +#if PY_VERSION_HEX >= 0x02060000 + Py_SIZE(result) = +#else + result->ob_size = +#endif + -(static_cast<int>(offsetof(instance<>,storage) + instance_size)); + } + return (PyObject*)result; + } + + static PyObject* instance_get_dict(PyObject* op, void*) + { + instance<>* inst = downcast<instance<> >(op); + if (inst->dict == 0) + inst->dict = PyDict_New(); + return python::xincref(inst->dict); + } + + static int instance_set_dict(PyObject* op, PyObject* dict, void*) + { + instance<>* inst = downcast<instance<> >(op); + python::xdecref(inst->dict); + inst->dict = python::incref(dict); + return 0; + } + + } + + + static PyGetSetDef instance_getsets[] = { + {const_cast<char*>("__dict__"), instance_get_dict, instance_set_dict, NULL, 0}, + {0, 0, 0, 0, 0} + }; + + + static PyMemberDef instance_members[] = { + {const_cast<char*>("__weakref__"), T_OBJECT, offsetof(instance<>, weakrefs), 0, 0}, + {0, 0, 0, 0, 0} + }; + + static PyTypeObject class_type_object = { + PyVarObject_HEAD_INIT(NULL, 0) + const_cast<char*>("Boost.Python.instance"), + offsetof(instance<>,storage), /* tp_basicsize */ + 1, /* tp_itemsize */ + instance_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT // | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(instance<>,weakrefs), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + instance_members, /* tp_members */ + instance_getsets, /* tp_getset */ + 0, //&PyBaseObject_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(instance<>,dict), /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + instance_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#if PYTHON_API_VERSION >= 1012 + 0 /* tp_del */ +#endif + }; + + BOOST_PYTHON_DECL type_handle class_type() + { + if (class_type_object.tp_dict == 0) + { + Py_TYPE(&class_type_object) = incref(class_metatype().get()); + class_type_object.tp_base = &PyBaseObject_Type; + if (PyType_Ready(&class_type_object)) + return type_handle(); +// class_type_object.tp_setattro = class_setattro; + } + return type_handle(borrowed(&class_type_object)); + } + + BOOST_PYTHON_DECL void* + find_instance_impl(PyObject* inst, type_info type, bool null_shared_ptr_only) + { + if (!Py_TYPE(Py_TYPE(inst)) || + !PyType_IsSubtype(Py_TYPE(Py_TYPE(inst)), &class_metatype_object)) + return 0; + + instance<>* self = reinterpret_cast<instance<>*>(inst); + + for (instance_holder* match = self->objects; match != 0; match = match->next()) + { + void* const found = match->holds(type, null_shared_ptr_only); + if (found) + return found; + } + return 0; + } + + object module_prefix() + { + return object( + PyObject_IsInstance(scope().ptr(), upcast<PyObject>(&PyModule_Type)) + ? object(scope().attr("__name__")) + : api::getattr(scope(), "__module__", str()) + ); + } + + namespace + { + // Find a registered class object corresponding to id. Return a + // null handle if no such class is registered. + inline type_handle query_class(type_info id) + { + converter::registration const* p = converter::registry::query(id); + return type_handle( + python::borrowed( + python::allow_null(p ? p->m_class_object : 0)) + ); + } + + // Find a registered class corresponding to id. If not found, + // throw an appropriate exception. + type_handle get_class(type_info id) + { + type_handle result(query_class(id)); + + if (result.get() == 0) + { + object report("extension class wrapper for base class "); + report = report + id.name() + " has not been created yet"; + PyErr_SetObject(PyExc_RuntimeError, report.ptr()); + throw_error_already_set(); + } + return result; + } + + // class_base constructor + // + // name - the name of the new Python class + // + // num_types - one more than the number of declared bases + // + // types - array of python::type_info, the first item + // corresponding to the class being created, and the + // rest corresponding to its declared bases. + // + inline object + new_class(char const* name, std::size_t num_types, type_info const* const types, char const* doc) + { + assert(num_types >= 1); + + // Build a tuple of the base Python type objects. If no bases + // were declared, we'll use our class_type() as the single base + // class. + ssize_t const num_bases = (std::max)(num_types - 1, static_cast<std::size_t>(1)); + handle<> bases(PyTuple_New(num_bases)); + + for (ssize_t i = 1; i <= num_bases; ++i) + { + type_handle c = (i >= static_cast<ssize_t>(num_types)) ? class_type() : get_class(types[i]); + // PyTuple_SET_ITEM steals this reference + PyTuple_SET_ITEM(bases.get(), static_cast<ssize_t>(i - 1), upcast<PyObject>(c.release())); + } + + // Call the class metatype to create a new class + dict d; + + object m = module_prefix(); + if (m) d["__module__"] = m; + + if (doc != 0) + d["__doc__"] = doc; + + object result = object(class_metatype())(name, bases, d); + assert(PyType_IsSubtype(Py_TYPE(result.ptr()), &PyType_Type)); + + if (scope().ptr() != Py_None) + scope().attr(name) = result; + + // For pickle. Will lead to informative error messages if pickling + // is not enabled. + result.attr("__reduce__") = object(make_instance_reduce_function()); + + return result; + } + } + + class_base::class_base( + char const* name, std::size_t num_types, type_info const* const types, char const* doc) + : object(new_class(name, num_types, types, doc)) + { + // Insert the new class object in the registry + converter::registration& converters = const_cast<converter::registration&>( + converter::registry::lookup(types[0])); + + // Class object is leaked, for now + converters.m_class_object = (PyTypeObject*)incref(this->ptr()); + } + + BOOST_PYTHON_DECL void copy_class_object(type_info const& src, type_info const& dst) + { + converter::registration& dst_converters + = const_cast<converter::registration&>(converter::registry::lookup(dst)); + + converter::registration const& src_converters = converter::registry::lookup(src); + + dst_converters.m_class_object = src_converters.m_class_object; + } + + void class_base::set_instance_size(std::size_t instance_size) + { + this->attr("__instance_size__") = instance_size; + } + + void class_base::add_property( + char const* name, object const& fget, char const* docstr) + { + object property( + (python::detail::new_reference) + PyObject_CallFunction((PyObject*)&PyProperty_Type, const_cast<char*>("Osss"), fget.ptr(), 0, 0, docstr)); + + this->setattr(name, property); + } + + void class_base::add_property( + char const* name, object const& fget, object const& fset, char const* docstr) + { + object property( + (python::detail::new_reference) + PyObject_CallFunction((PyObject*)&PyProperty_Type, const_cast<char*>("OOss"), fget.ptr(), fset.ptr(), 0, docstr)); + + this->setattr(name, property); + } + + void class_base::add_static_property(char const* name, object const& fget) + { + object property( + (python::detail::new_reference) + PyObject_CallFunction(static_data(), const_cast<char*>("O"), fget.ptr()) + ); + + this->setattr(name, property); + } + + void class_base::add_static_property(char const* name, object const& fget, object const& fset) + { + object property( + (python::detail::new_reference) + PyObject_CallFunction(static_data(), const_cast<char*>("OO"), fget.ptr(), fset.ptr())); + + this->setattr(name, property); + } + + void class_base::setattr(char const* name, object const& x) + { + if (PyObject_SetAttrString(this->ptr(), const_cast<char*>(name), x.ptr()) < 0) + throw_error_already_set(); + } + + namespace + { + extern "C" PyObject* no_init(PyObject*, PyObject*) + { + ::PyErr_SetString(::PyExc_RuntimeError, const_cast<char*>("This class cannot be instantiated from Python")); + return NULL; + } + static ::PyMethodDef no_init_def = { + const_cast<char*>("__init__"), no_init, METH_VARARGS, + const_cast<char*>("Raises an exception\n" + "This class cannot be instantiated from Python\n") + }; + } + + void class_base::def_no_init() + { + handle<> f(::PyCFunction_New(&no_init_def, 0)); + this->setattr("__init__", object(f)); + } + + void class_base::enable_pickling_(bool getstate_manages_dict) + { + setattr("__safe_for_unpickling__", object(true)); + + if (getstate_manages_dict) + { + setattr("__getstate_manages_dict__", object(true)); + } + } + + namespace + { + PyObject* callable_check(PyObject* callable) + { + if (PyCallable_Check(expect_non_null(callable))) + return callable; + + ::PyErr_Format( + PyExc_TypeError + , const_cast<char*>("staticmethod expects callable object; got an object of type %s, which is not callable") + , Py_TYPE(callable)->tp_name + ); + + throw_error_already_set(); + return 0; + } + } + + void class_base::make_method_static(const char * method_name) + { + PyTypeObject* self = downcast<PyTypeObject>(this->ptr()); + dict d((handle<>(borrowed(self->tp_dict)))); + + object method(d[method_name]); + + this->attr(method_name) = object( + handle<>( + PyStaticMethod_New((callable_check)(method.ptr()) ) + )); + } + + BOOST_PYTHON_DECL type_handle registered_class_object(type_info id) + { + return query_class(id); + } +} // namespace objects + + +void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std::size_t holder_size) +{ + assert(PyType_IsSubtype(Py_TYPE(Py_TYPE(self_)), &class_metatype_object)); + objects::instance<>* self = (objects::instance<>*)self_; + + int total_size_needed = holder_offset + holder_size; + + if (-Py_SIZE(self) >= total_size_needed) + { + // holder_offset should at least point into the variable-sized part + assert(holder_offset >= offsetof(objects::instance<>,storage)); + + // Record the fact that the storage is occupied, noting where it starts + Py_SIZE(self) = holder_offset; + return (char*)self + holder_offset; + } + else + { + void* const result = PyMem_Malloc(holder_size); + if (result == 0) + throw std::bad_alloc(); + return result; + } +} + +void instance_holder::deallocate(PyObject* self_, void* storage) throw() +{ + assert(PyType_IsSubtype(Py_TYPE(Py_TYPE(self_)), &class_metatype_object)); + objects::instance<>* self = (objects::instance<>*)self_; + if (storage != (char*)self + Py_SIZE(self)) + { + PyMem_Free(storage); + } +} + +}} // namespace boost::python diff --git a/libs/python/src/object/enum.cpp b/libs/python/src/object/enum.cpp new file mode 100644 index 000000000..3063320cb --- /dev/null +++ b/libs/python/src/object/enum.cpp @@ -0,0 +1,246 @@ +// Copyright David Abrahams 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/python/object/enum_base.hpp> +#include <boost/python/cast.hpp> +#include <boost/python/scope.hpp> +#include <boost/python/object.hpp> +#include <boost/python/tuple.hpp> +#include <boost/python/dict.hpp> +#include <boost/python/str.hpp> +#include <boost/python/extract.hpp> +#include <boost/python/object_protocol.hpp> +#include <structmember.h> + +namespace boost { namespace python { namespace objects { + +struct enum_object +{ +#if PY_VERSION_HEX >= 0x03000000 + PyLongObject base_object; +#else + PyIntObject base_object; +#endif + PyObject* name; +}; + +static PyMemberDef enum_members[] = { + {const_cast<char*>("name"), T_OBJECT_EX, offsetof(enum_object,name),READONLY, 0}, + {0, 0, 0, 0, 0} +}; + + +extern "C" +{ + static PyObject* enum_repr(PyObject* self_) + { + // XXX(bhy) Potentional memory leak here since PyObject_GetAttrString returns a new reference + // const char *mod = PyString_AsString(PyObject_GetAttrString( self_, const_cast<char*>("__module__"))); + PyObject *mod = PyObject_GetAttrString( self_, "__module__"); + enum_object* self = downcast<enum_object>(self_); + if (!self->name) + { + return +#if PY_VERSION_HEX >= 0x03000000 + PyUnicode_FromFormat("%S.%s(%ld)", mod, self_->ob_type->tp_name, PyLong_AsLong(self_)); +#else + PyString_FromFormat("%s.%s(%ld)", PyString_AsString(mod), self_->ob_type->tp_name, PyInt_AS_LONG(self_)); +#endif + } + else + { + PyObject* name = self->name; + if (name == 0) + return 0; + + return +#if PY_VERSION_HEX >= 0x03000000 + PyUnicode_FromFormat("%S.%s.%S", mod, self_->ob_type->tp_name, name); +#else + PyString_FromFormat("%s.%s.%s", + PyString_AsString(mod), self_->ob_type->tp_name, PyString_AsString(name)); +#endif + } + } + + static PyObject* enum_str(PyObject* self_) + { + enum_object* self = downcast<enum_object>(self_); + if (!self->name) + { +#if PY_VERSION_HEX >= 0x03000000 + return PyLong_Type.tp_str(self_); +#else + return PyInt_Type.tp_str(self_); +#endif + } + else + { + return incref(self->name); + } + } +} + +static PyTypeObject enum_type_object = { + PyVarObject_HEAD_INIT(NULL, 0) // &PyType_Type + const_cast<char*>("Boost.Python.enum"), + sizeof(enum_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + enum_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + enum_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT +#if PY_VERSION_HEX < 0x03000000 + | Py_TPFLAGS_CHECKTYPES +#endif + | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + enum_members, /* tp_members */ + 0, /* tp_getset */ + 0, //&PyInt_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#if PYTHON_API_VERSION >= 1012 + 0 /* tp_del */ +#endif +}; + +object module_prefix(); + +namespace +{ + object new_enum_type(char const* name, char const *doc) + { + if (enum_type_object.tp_dict == 0) + { + Py_TYPE(&enum_type_object) = incref(&PyType_Type); +#if PY_VERSION_HEX >= 0x03000000 + enum_type_object.tp_base = &PyLong_Type; +#else + enum_type_object.tp_base = &PyInt_Type; +#endif + if (PyType_Ready(&enum_type_object)) + throw_error_already_set(); + } + + type_handle metatype(borrowed(&PyType_Type)); + type_handle base(borrowed(&enum_type_object)); + + // suppress the instance __dict__ in these enum objects. There + // may be a slicker way, but this'll do for now. + dict d; + d["__slots__"] = tuple(); + d["values"] = dict(); + d["names"] = dict(); + + object module_name = module_prefix(); + if (module_name) + d["__module__"] = module_name; + if (doc) + d["__doc__"] = doc; + + object result = (object(metatype))(name, make_tuple(base), d); + + scope().attr(name) = result; + + return result; + } +} + +enum_base::enum_base( + char const* name + , converter::to_python_function_t to_python + , converter::convertible_function convertible + , converter::constructor_function construct + , type_info id + , char const *doc + ) + : object(new_enum_type(name, doc)) +{ + converter::registration& converters + = const_cast<converter::registration&>( + converter::registry::lookup(id)); + + converters.m_class_object = downcast<PyTypeObject>(this->ptr()); + converter::registry::insert(to_python, id); + converter::registry::insert(convertible, construct, id); +} + +void enum_base::add_value(char const* name_, long value) +{ + // Convert name to Python string + object name(name_); + + // Create a new enum instance by calling the class with a value + object x = (*this)(value); + + // Store the object in the enum class + (*this).attr(name_) = x; + + dict d = extract<dict>(this->attr("values"))(); + d[value] = x; + + // Set the name field in the new enum instanec + enum_object* p = downcast<enum_object>(x.ptr()); + Py_XDECREF(p->name); + p->name = incref(name.ptr()); + + dict names_dict = extract<dict>(this->attr("names"))(); + names_dict[x.attr("name")] = x; +} + +void enum_base::export_values() +{ + dict d = extract<dict>(this->attr("names"))(); + list items = d.items(); + scope current; + + for (unsigned i = 0, max = len(items); i < max; ++i) + api::setattr(current, items[i][0], items[i][1]); + } + +PyObject* enum_base::to_python(PyTypeObject* type_, long x) +{ + object type((type_handle(borrowed(type_)))); + + dict d = extract<dict>(type.attr("values"))(); + object v = d.get(x, object()); + return incref( + (v == object() ? type(x) : v).ptr()); +} + +}}} // namespace boost::python::object diff --git a/libs/python/src/object/function.cpp b/libs/python/src/object/function.cpp new file mode 100644 index 000000000..5c59cc779 --- /dev/null +++ b/libs/python/src/object/function.cpp @@ -0,0 +1,793 @@ +// Copyright David Abrahams 2001. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/python/docstring_options.hpp> +#include <boost/python/object/function_object.hpp> +#include <boost/python/object/function_handle.hpp> +#include <boost/python/object/function_doc_signature.hpp> +#include <boost/python/errors.hpp> +#include <boost/python/str.hpp> +#include <boost/python/object_attributes.hpp> +#include <boost/python/args.hpp> +#include <boost/python/refcount.hpp> +#include <boost/python/extract.hpp> +#include <boost/python/tuple.hpp> +#include <boost/python/list.hpp> +#include <boost/python/ssize_t.hpp> + +#include <boost/python/detail/signature.hpp> +#include <boost/python/detail/none.hpp> +#include <boost/mpl/vector/vector10.hpp> + +#include <boost/bind.hpp> + +#include <algorithm> +#include <cstring> + +#if BOOST_PYTHON_DEBUG_ERROR_MESSAGES +# include <cstdio> +#endif + +namespace boost { namespace python { + volatile bool docstring_options::show_user_defined_ = true; + volatile bool docstring_options::show_cpp_signatures_ = true; +#ifndef BOOST_PYTHON_NO_PY_SIGNATURES + volatile bool docstring_options::show_py_signatures_ = true; +#else + volatile bool docstring_options::show_py_signatures_ = false; +#endif +}} + +namespace boost { namespace python { namespace objects { + +py_function_impl_base::~py_function_impl_base() +{ +} + +unsigned py_function_impl_base::max_arity() const +{ + return this->min_arity(); +} + +extern PyTypeObject function_type; + +function::function( + py_function const& implementation +#if BOOST_WORKAROUND(__EDG_VERSION__, == 245) + , python::detail::keyword const* names_and_defaults +#else + , python::detail::keyword const* const names_and_defaults +#endif + , unsigned num_keywords + ) + : m_fn(implementation) + , m_nkeyword_values(0) +{ + if (names_and_defaults != 0) + { + unsigned int max_arity = m_fn.max_arity(); + unsigned int keyword_offset + = max_arity > num_keywords ? max_arity - num_keywords : 0; + + + ssize_t tuple_size = num_keywords ? max_arity : 0; + m_arg_names = object(handle<>(PyTuple_New(tuple_size))); + + if (num_keywords != 0) + { + for (unsigned j = 0; j < keyword_offset; ++j) + PyTuple_SET_ITEM(m_arg_names.ptr(), j, incref(Py_None)); + } + + for (unsigned i = 0; i < num_keywords; ++i) + { + tuple kv; + + python::detail::keyword const* const p = names_and_defaults + i; + if (p->default_value) + { + kv = make_tuple(p->name, p->default_value); + ++m_nkeyword_values; + } + else + { + kv = make_tuple(p->name); + } + + PyTuple_SET_ITEM( + m_arg_names.ptr() + , i + keyword_offset + , incref(kv.ptr()) + ); + } + } + + PyObject* p = this; + if (Py_TYPE(&function_type) == 0) + { + Py_TYPE(&function_type) = &PyType_Type; + ::PyType_Ready(&function_type); + } + + (void)( // warning suppression for GCC + PyObject_INIT(p, &function_type) + ); +} + +function::~function() +{ +} + +PyObject* function::call(PyObject* args, PyObject* keywords) const +{ + std::size_t n_unnamed_actual = PyTuple_GET_SIZE(args); + std::size_t n_keyword_actual = keywords ? PyDict_Size(keywords) : 0; + std::size_t n_actual = n_unnamed_actual + n_keyword_actual; + + function const* f = this; + + // Try overloads looking for a match + do + { + // Check for a plausible number of arguments + unsigned min_arity = f->m_fn.min_arity(); + unsigned max_arity = f->m_fn.max_arity(); + + if (n_actual + f->m_nkeyword_values >= min_arity + && n_actual <= max_arity) + { + // This will be the args that actually get passed + handle<>inner_args(allow_null(borrowed(args))); + + if (n_keyword_actual > 0 // Keyword arguments were supplied + || n_actual < min_arity) // or default keyword values are needed + { + if (f->m_arg_names.is_none()) + { + // this overload doesn't accept keywords + inner_args = handle<>(); + } + else + { + // "all keywords are none" is a special case + // indicating we will accept any number of keyword + // arguments + if (PyTuple_Size(f->m_arg_names.ptr()) == 0) + { + // no argument preprocessing + } + else if (n_actual > max_arity) + { + // too many arguments + inner_args = handle<>(); + } + else + { + // build a new arg tuple, will adjust its size later + assert(max_arity <= static_cast<std::size_t>(ssize_t_max)); + inner_args = handle<>( + PyTuple_New(static_cast<ssize_t>(max_arity))); + + // Fill in the positional arguments + for (std::size_t i = 0; i < n_unnamed_actual; ++i) + PyTuple_SET_ITEM(inner_args.get(), i, incref(PyTuple_GET_ITEM(args, i))); + + // Grab remaining arguments by name from the keyword dictionary + std::size_t n_actual_processed = n_unnamed_actual; + + for (std::size_t arg_pos = n_unnamed_actual; arg_pos < max_arity ; ++arg_pos) + { + // Get the keyword[, value pair] corresponding + PyObject* kv = PyTuple_GET_ITEM(f->m_arg_names.ptr(), arg_pos); + + // If there were any keyword arguments, + // look up the one we need for this + // argument position + PyObject* value = n_keyword_actual + ? PyDict_GetItem(keywords, PyTuple_GET_ITEM(kv, 0)) + : 0; + + if (!value) + { + // Not found; check if there's a default value + if (PyTuple_GET_SIZE(kv) > 1) + value = PyTuple_GET_ITEM(kv, 1); + + if (!value) + { + // still not found; matching fails + PyErr_Clear(); + inner_args = handle<>(); + break; + } + } + else + { + ++n_actual_processed; + } + + PyTuple_SET_ITEM(inner_args.get(), arg_pos, incref(value)); + } + + if (inner_args.get()) + { + //check if we proccessed all the arguments + if(n_actual_processed < n_actual) + inner_args = handle<>(); + } + } + } + } + + // Call the function. Pass keywords in case it's a + // function accepting any number of keywords + PyObject* result = inner_args ? f->m_fn(inner_args.get(), keywords) : 0; + + // If the result is NULL but no error was set, m_fn failed + // the argument-matching test. + + // This assumes that all other error-reporters are + // well-behaved and never return NULL to python without + // setting an error. + if (result != 0 || PyErr_Occurred()) + return result; + } + f = f->m_overloads.get(); + } + while (f); + // None of the overloads matched; time to generate the error message + argument_error(args, keywords); + return 0; +} + +object function::signature(bool show_return_type) const +{ + py_function const& impl = m_fn; + + python::detail::signature_element const* return_type = impl.signature(); + python::detail::signature_element const* s = return_type + 1; + + list formal_params; + if (impl.max_arity() == 0) + formal_params.append("void"); + + for (unsigned n = 0; n < impl.max_arity(); ++n) + { + if (s[n].basename == 0) + { + formal_params.append("..."); + break; + } + + str param(s[n].basename); + if (s[n].lvalue) + param += " {lvalue}"; + + if (m_arg_names) // None or empty tuple will test false + { + object kv(m_arg_names[n]); + if (kv) + { + char const* const fmt = len(kv) > 1 ? " %s=%r" : " %s"; + param += fmt % kv; + } + } + + formal_params.append(param); + } + + if (show_return_type) + return "%s(%s) -> %s" % make_tuple( + m_name, str(", ").join(formal_params), return_type->basename); + return "%s(%s)" % make_tuple( + m_name, str(", ").join(formal_params)); +} + +object function::signatures(bool show_return_type) const +{ + list result; + for (function const* f = this; f; f = f->m_overloads.get()) { + result.append(f->signature(show_return_type)); + } + return result; +} + +void function::argument_error(PyObject* args, PyObject* /*keywords*/) const +{ + static handle<> exception( + PyErr_NewException(const_cast<char*>("Boost.Python.ArgumentError"), PyExc_TypeError, 0)); + + object message = "Python argument types in\n %s.%s(" + % make_tuple(this->m_namespace, this->m_name); + + list actual_args; + for (ssize_t i = 0; i < PyTuple_Size(args); ++i) + { + char const* name = PyTuple_GetItem(args, i)->ob_type->tp_name; + actual_args.append(str(name)); + } + message += str(", ").join(actual_args); + message += ")\ndid not match C++ signature:\n "; + message += str("\n ").join(signatures()); + +#if BOOST_PYTHON_DEBUG_ERROR_MESSAGES + std::printf("\n--------\n%s\n--------\n", extract<const char*>(message)()); +#endif + PyErr_SetObject(exception.get(), message.ptr()); + throw_error_already_set(); +} + +void function::add_overload(handle<function> const& overload_) +{ + function* parent = this; + + while (parent->m_overloads) + parent = parent->m_overloads.get(); + + parent->m_overloads = overload_; + + // If we have no documentation, get the docs from the overload + if (!m_doc) + m_doc = overload_->m_doc; +} + +namespace +{ + char const* const binary_operator_names[] = + { + "add__", + "and__", + "div__", + "divmod__", + "eq__", + "floordiv__", + "ge__", + "gt__", + "le__", + "lshift__", + "lt__", + "mod__", + "mul__", + "ne__", + "or__", + "pow__", + "radd__", + "rand__", + "rdiv__", + "rdivmod__", + "rfloordiv__", + "rlshift__", + "rmod__", + "rmul__", + "ror__", + "rpow__", + "rrshift__", + "rshift__", + "rsub__", + "rtruediv__", + "rxor__", + "sub__", + "truediv__", + "xor__" + }; + + struct less_cstring + { + bool operator()(char const* x, char const* y) const + { + return BOOST_CSTD_::strcmp(x,y) < 0; + } + }; + + inline bool is_binary_operator(char const* name) + { + return name[0] == '_' + && name[1] == '_' + && std::binary_search( + &binary_operator_names[0] + , binary_operator_names + sizeof(binary_operator_names)/sizeof(*binary_operator_names) + , name + 2 + , less_cstring() + ); + } + + // Something for the end of the chain of binary operators + PyObject* not_implemented(PyObject*, PyObject*) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + handle<function> not_implemented_function() + { + + static object keeper( + function_object( + py_function(¬_implemented, mpl::vector1<void>(), 2) + , python::detail::keyword_range()) + ); + return handle<function>(borrowed(downcast<function>(keeper.ptr()))); + } +} + +void function::add_to_namespace( + object const& name_space, char const* name_, object const& attribute) +{ + add_to_namespace(name_space, name_, attribute, 0); +} + +namespace detail +{ + extern char py_signature_tag[]; + extern char cpp_signature_tag[]; +} + +void function::add_to_namespace( + object const& name_space, char const* name_, object const& attribute, char const* doc) +{ + str const name(name_); + PyObject* const ns = name_space.ptr(); + + if (attribute.ptr()->ob_type == &function_type) + { + function* new_func = downcast<function>(attribute.ptr()); + handle<> dict; + +#if PY_VERSION_HEX < 0x03000000 + // Old-style class gone in Python 3 + if (PyClass_Check(ns)) + dict = handle<>(borrowed(((PyClassObject*)ns)->cl_dict)); + else +#endif + if (PyType_Check(ns)) + dict = handle<>(borrowed(((PyTypeObject*)ns)->tp_dict)); + else + dict = handle<>(PyObject_GetAttrString(ns, const_cast<char*>("__dict__"))); + + if (dict == 0) + throw_error_already_set(); + + handle<> existing(allow_null(::PyObject_GetItem(dict.get(), name.ptr()))); + + if (existing) + { + if (existing->ob_type == &function_type) + { + new_func->add_overload( + handle<function>( + borrowed( + downcast<function>(existing.get()) + ) + ) + ); + } + else if (existing->ob_type == &PyStaticMethod_Type) + { + char const* name_space_name = extract<char const*>(name_space.attr("__name__")); + + ::PyErr_Format( + PyExc_RuntimeError + , "Boost.Python - All overloads must be exported " + "before calling \'class_<...>(\"%s\").staticmethod(\"%s\")\'" + , name_space_name + , name_ + ); + throw_error_already_set(); + } + } + else if (is_binary_operator(name_)) + { + // Binary operators need an additional overload which + // returns NotImplemented, so that Python will try the + // __rxxx__ functions on the other operand. We add this + // when no overloads for the operator already exist. + new_func->add_overload(not_implemented_function()); + } + + // A function is named the first time it is added to a namespace. + if (new_func->name().is_none()) + new_func->m_name = name; + + handle<> name_space_name( + allow_null(::PyObject_GetAttrString(name_space.ptr(), const_cast<char*>("__name__")))); + + if (name_space_name) + new_func->m_namespace = object(name_space_name); + } + + // The PyObject_GetAttrString() or PyObject_GetItem calls above may + // have left an active error + PyErr_Clear(); + if (PyObject_SetAttr(ns, name.ptr(), attribute.ptr()) < 0) + throw_error_already_set(); + + object mutable_attribute(attribute); +/* + if (doc != 0 && docstring_options::show_user_defined_) + { + // Accumulate documentation + + if ( + PyObject_HasAttrString(mutable_attribute.ptr(), "__doc__") + && mutable_attribute.attr("__doc__")) + { + mutable_attribute.attr("__doc__") += "\n\n"; + mutable_attribute.attr("__doc__") += doc; + } + else { + mutable_attribute.attr("__doc__") = doc; + } + } + + if (docstring_options::show_signatures_) + { + if ( PyObject_HasAttrString(mutable_attribute.ptr(), "__doc__") + && mutable_attribute.attr("__doc__")) { + mutable_attribute.attr("__doc__") += ( + mutable_attribute.attr("__doc__")[-1] != "\n" ? "\n\n" : "\n"); + } + else { + mutable_attribute.attr("__doc__") = ""; + } + function* f = downcast<function>(attribute.ptr()); + mutable_attribute.attr("__doc__") += str("\n ").join(make_tuple( + "C++ signature:", f->signature(true))); + } + */ + str _doc; + + if (docstring_options::show_py_signatures_) + { + _doc += str(const_cast<const char*>(detail::py_signature_tag)); + } + if (doc != 0 && docstring_options::show_user_defined_) + _doc += doc; + + if (docstring_options::show_cpp_signatures_) + { + _doc += str(const_cast<const char*>(detail::cpp_signature_tag)); + } + if(_doc) + { + object mutable_attribute(attribute); + mutable_attribute.attr("__doc__")= _doc; + } +} + +BOOST_PYTHON_DECL void add_to_namespace( + object const& name_space, char const* name, object const& attribute) +{ + function::add_to_namespace(name_space, name, attribute, 0); +} + +BOOST_PYTHON_DECL void add_to_namespace( + object const& name_space, char const* name, object const& attribute, char const* doc) +{ + function::add_to_namespace(name_space, name, attribute, doc); +} + + +namespace +{ + struct bind_return + { + bind_return(PyObject*& result, function const* f, PyObject* args, PyObject* keywords) + : m_result(result) + , m_f(f) + , m_args(args) + , m_keywords(keywords) + {} + + void operator()() const + { + m_result = m_f->call(m_args, m_keywords); + } + + private: + PyObject*& m_result; + function const* m_f; + PyObject* m_args; + PyObject* m_keywords; + }; +} + +extern "C" +{ + // Stolen from Python's funcobject.c + static PyObject * + function_descr_get(PyObject *func, PyObject *obj, PyObject *type_) + { +#if PY_VERSION_HEX >= 0x03000000 + // The implement is different in Python 3 because of the removal of unbound method + if (obj == Py_None || obj == NULL) { + Py_INCREF(func); + return func; + } + return PyMethod_New(func, obj); +#else + if (obj == Py_None) + obj = NULL; + return PyMethod_New(func, obj, type_); +#endif + } + + static void + function_dealloc(PyObject* p) + { + delete static_cast<function*>(p); + } + + static PyObject * + function_call(PyObject *func, PyObject *args, PyObject *kw) + { + PyObject* result = 0; + handle_exception(bind_return(result, static_cast<function*>(func), args, kw)); + return result; + } + + // + // Here we're using the function's tp_getset rather than its + // tp_members to set up __doc__ and __name__, because tp_members + // really depends on having a POD object type (it relies on + // offsets). It might make sense to reformulate function as a POD + // at some point, but this is much more expedient. + // + static PyObject* function_get_doc(PyObject* op, void*) + { + function* f = downcast<function>(op); + list signatures = function_doc_signature_generator::function_doc_signatures(f); + if(!signatures) return python::detail::none(); + signatures.reverse(); + return python::incref( str("\n").join(signatures).ptr()); + } + + static int function_set_doc(PyObject* op, PyObject* doc, void*) + { + function* f = downcast<function>(op); + f->doc(doc ? object(python::detail::borrowed_reference(doc)) : object()); + return 0; + } + + static PyObject* function_get_name(PyObject* op, void*) + { + function* f = downcast<function>(op); + if (f->name().is_none()) +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_InternFromString("<unnamed Boost.Python function>"); +#else + return PyString_InternFromString("<unnamed Boost.Python function>"); +#endif + else + return python::incref(f->name().ptr()); + } + + // We add a dummy __class__ attribute in order to fool PyDoc into + // treating these as built-in functions and scanning their + // documentation + static PyObject* function_get_class(PyObject* /*op*/, void*) + { + return python::incref(upcast<PyObject>(&PyCFunction_Type)); + } + + static PyObject* function_get_module(PyObject* op, void*) + { + function* f = downcast<function>(op); + object const& ns = f->get_namespace(); + if (!ns.is_none()) { + return python::incref(ns.ptr()); + } + PyErr_SetString( + PyExc_AttributeError, const_cast<char*>( + "Boost.Python function __module__ unknown.")); + return 0; + } +} + +static PyGetSetDef function_getsetlist[] = { + {const_cast<char*>("__name__"), (getter)function_get_name, 0, 0, 0 }, + {const_cast<char*>("func_name"), (getter)function_get_name, 0, 0, 0 }, + {const_cast<char*>("__module__"), (getter)function_get_module, 0, 0, 0 }, + {const_cast<char*>("func_module"), (getter)function_get_module, 0, 0, 0 }, + {const_cast<char*>("__class__"), (getter)function_get_class, 0, 0, 0 }, // see note above + {const_cast<char*>("__doc__"), (getter)function_get_doc, (setter)function_set_doc, 0, 0}, + {const_cast<char*>("func_doc"), (getter)function_get_doc, (setter)function_set_doc, 0, 0}, + {NULL, 0, 0, 0, 0} /* Sentinel */ +}; + +PyTypeObject function_type = { + PyVarObject_HEAD_INIT(NULL, 0) + const_cast<char*>("Boost.Python.function"), + sizeof(function), + 0, + (destructor)function_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, //(reprfunc)func_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + function_call, /* tp_call */ + 0, /* tp_str */ + 0, // PyObject_GenericGetAttr, /* tp_getattro */ + 0, // PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */ + 0, /* tp_doc */ + 0, // (traverseproc)func_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, //offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, // func_memberlist, /* tp_members */ + function_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + function_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, //offsetof(PyFunctionObject, func_dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#if PYTHON_API_VERSION >= 1012 + 0 /* tp_del */ +#endif +}; + +object function_object( + py_function const& f + , python::detail::keyword_range const& keywords) +{ + return python::object( + python::detail::new_non_null_reference( + new function( + f, keywords.first, keywords.second - keywords.first))); +} + +object function_object(py_function const& f) +{ + return function_object(f, python::detail::keyword_range()); +} + + +handle<> function_handle_impl(py_function const& f) +{ + return python::handle<>( + allow_null( + new function(f, 0, 0))); +} + +} // namespace objects + +namespace detail +{ + object BOOST_PYTHON_DECL make_raw_function(objects::py_function f) + { + static keyword k; + + return objects::function_object( + f + , keyword_range(&k,&k)); + } + void BOOST_PYTHON_DECL pure_virtual_called() + { + PyErr_SetString( + PyExc_RuntimeError, const_cast<char*>("Pure virtual function called")); + throw_error_already_set(); + } +} + +}} // namespace boost::python diff --git a/libs/python/src/object/function_doc_signature.cpp b/libs/python/src/object/function_doc_signature.cpp new file mode 100644 index 000000000..41695285a --- /dev/null +++ b/libs/python/src/object/function_doc_signature.cpp @@ -0,0 +1,344 @@ +// Copyright Nikolay Mladenov 2007. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// boost::python::make_tuple below are for gcc 4.4 -std=c++0x compatibility +// (Intel C++ 10 and 11 with -std=c++0x don't need the full qualification). + +#include <boost/python/converter/registrations.hpp> +#include <boost/python/object/function_doc_signature.hpp> +#include <boost/python/errors.hpp> +#include <boost/python/str.hpp> +#include <boost/python/args.hpp> +#include <boost/python/tuple.hpp> + +#include <boost/python/detail/signature.hpp> + +#include <vector> + +namespace boost { namespace python { namespace objects { + + bool function_doc_signature_generator::arity_cmp( function const *f1, function const *f2 ) + { + return f1->m_fn.max_arity() < f2->m_fn.max_arity(); + } + + bool function_doc_signature_generator::are_seq_overloads( function const *f1, function const *f2 , bool check_docs) + { + py_function const & impl1 = f1->m_fn; + py_function const & impl2 = f2->m_fn; + + //the number of parameters differs by 1 + if (impl2.max_arity()-impl1.max_arity() != 1) + return false; + + // if check docs then f1 shold not have docstring or have the same docstring as f2 + if (check_docs && f2->doc() != f1->doc() && f1->doc()) + return false; + + python::detail::signature_element const* s1 = impl1.signature(); + python::detail::signature_element const* s2 = impl2.signature(); + + unsigned size = impl1.max_arity()+1; + + for (unsigned i = 0; i != size; ++i) + { + //check if the argument types are the same + if (s1[i].basename != s2[i].basename) + return false; + + //return type + if (!i) continue; + + //check if the argument default values are the same + bool f1_has_names = bool(f1->m_arg_names); + bool f2_has_names = bool(f2->m_arg_names); + if ( (f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=f1->m_arg_names[i-1]) + || (f1_has_names && !f2_has_names) + || (!f1_has_names && f2_has_names && f2->m_arg_names[i-1]!=python::object()) + ) + return false; + } + return true; + } + + std::vector<function const*> function_doc_signature_generator::flatten(function const *f) + { + object name = f->name(); + + std::vector<function const*> res; + + while (f) { + + //this if takes out the not_implemented_function + if (f->name() == name) + res.push_back(f); + + f=f->m_overloads.get(); + } + + //std::sort(res.begin(),res.end(), &arity_cmp); + + return res; + } + std::vector<function const*> function_doc_signature_generator::split_seq_overloads( const std::vector<function const *> &funcs, bool split_on_doc_change) + { + std::vector<function const*> res; + + std::vector<function const*>::const_iterator fi = funcs.begin(); + + function const * last = *fi; + + while (++fi != funcs.end()){ + + //check if fi starts a new chain of overloads + if (!are_seq_overloads( last, *fi, split_on_doc_change )) + res.push_back(last); + + last = *fi; + } + + if (last) + res.push_back(last); + + return res; + } + + str function_doc_signature_generator::raw_function_pretty_signature(function const *f, size_t n_overloads, bool cpp_types ) + { + str res("object"); + + res = str("%s %s(%s)" % make_tuple( res, f->m_name, str("tuple args, dict kwds")) ); + + return res; + } + + const char * function_doc_signature_generator::py_type_str(const python::detail::signature_element &s) + { + if (s.basename==std::string("void")){ + static const char * none = "None"; + return none; + } + + PyTypeObject const * py_type = s.pytype_f?s.pytype_f():0; + if ( py_type ) + return py_type->tp_name; + else{ + static const char * object = "object"; + return object; + } + } + + str function_doc_signature_generator::parameter_string(py_function const &f, size_t n, object arg_names, bool cpp_types) + { + str param; + + python::detail::signature_element const * s = f.signature(); + if (cpp_types) + { + if(!n) + s = &f.get_return_type(); + if (s[n].basename == 0) + { + return str("..."); + } + + param = str(s[n].basename); + + if (s[n].lvalue) + param += " {lvalue}"; + + } + else + { + if (n) //we are processing an argument and trying to come up with a name for it + { + object kv; + if ( arg_names && (kv = arg_names[n-1]) ) + param = str( " (%s)%s" % make_tuple(py_type_str(s[n]),kv[0]) ); + else + param = str(" (%s)%s%d" % make_tuple(py_type_str(s[n]),"arg", n) ); + } + else //we are processing the return type + param = py_type_str(f.get_return_type()); + } + + //an argument - check for default value and append it + if(n && arg_names) + { + object kv(arg_names[n-1]); + if (kv && len(kv) == 2) + { + param = str("%s=%r" % make_tuple(param, kv[1])); + } + } + return param; + } + + str function_doc_signature_generator::pretty_signature(function const *f, size_t n_overloads, bool cpp_types ) + { + py_function + const& impl = f->m_fn; + ; + + + unsigned arity = impl.max_arity(); + + if(arity == unsigned(-1))// is this the proper raw function test? + { + return raw_function_pretty_signature(f,n_overloads,cpp_types); + } + + list formal_params; + + size_t n_extra_default_args=0; + + for (unsigned n = 0; n <= arity; ++n) + { + str param; + + formal_params.append( + parameter_string(impl, n, f->m_arg_names, cpp_types) + ); + + // find all the arguments with default values preceeding the arity-n_overloads + if (n && f->m_arg_names) + { + object kv(f->m_arg_names[n-1]); + + if (kv && len(kv) == 2) + { + //default argument preceeding the arity-n_overloads + if( n <= arity-n_overloads) + ++n_extra_default_args; + } + else + //argument without default, preceeding the arity-n_overloads + if( n <= arity-n_overloads) + n_extra_default_args = 0; + } + } + + n_overloads+=n_extra_default_args; + + if (!arity && cpp_types) + formal_params.append("void"); + + str ret_type (formal_params.pop(0)); + if (cpp_types ) + { + return str( + "%s %s(%s%s%s%s)" + % boost::python::make_tuple // workaround, see top + ( ret_type + , f->m_name + , str(",").join(formal_params.slice(0,arity-n_overloads)) + , n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str() + , str(" [,").join(formal_params.slice(arity-n_overloads,arity)) + , std::string(n_overloads,']') + )); + }else{ + return str( + "%s(%s%s%s%s) -> %s" + % boost::python::make_tuple // workaround, see top + ( f->m_name + , str(",").join(formal_params.slice(0,arity-n_overloads)) + , n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str() + , str(" [,").join(formal_params.slice(arity-n_overloads,arity)) + , std::string(n_overloads,']') + , ret_type + )); + } + + return str( + "%s %s(%s%s%s%s) %s" + % boost::python::make_tuple // workaround, see top + ( cpp_types?ret_type:str("") + , f->m_name + , str(",").join(formal_params.slice(0,arity-n_overloads)) + , n_overloads ? (n_overloads!=arity?str(" [,"):str("[ ")) : str() + , str(" [,").join(formal_params.slice(arity-n_overloads,arity)) + , std::string(n_overloads,']') + , cpp_types?str(""):ret_type + )); + + } + + namespace detail { + char py_signature_tag[] = "PY signature :"; + char cpp_signature_tag[] = "C++ signature :"; + } + + list function_doc_signature_generator::function_doc_signatures( function const * f) + { + list signatures; + std::vector<function const*> funcs = flatten( f); + std::vector<function const*> split_funcs = split_seq_overloads( funcs, true); + std::vector<function const*>::const_iterator sfi=split_funcs.begin(), fi; + size_t n_overloads=0; + for (fi=funcs.begin(); fi!=funcs.end(); ++fi) + { + if(*sfi == *fi){ + if((*fi)->doc()) + { + str func_doc = str((*fi)->doc()); + + int doc_len = len(func_doc); + + bool show_py_signature = doc_len >= int(sizeof(detail::py_signature_tag)/sizeof(char)-1) + && str(detail::py_signature_tag) == func_doc.slice(0, int(sizeof(detail::py_signature_tag)/sizeof(char))-1); + if(show_py_signature) + { + func_doc = str(func_doc.slice(int(sizeof(detail::py_signature_tag)/sizeof(char))-1, _)); + doc_len = len(func_doc); + } + + bool show_cpp_signature = doc_len >= int(sizeof(detail::cpp_signature_tag)/sizeof(char)-1) + && str(detail::cpp_signature_tag) == func_doc.slice( 1-int(sizeof(detail::cpp_signature_tag)/sizeof(char)), _); + + if(show_cpp_signature) + { + func_doc = str(func_doc.slice(_, 1-int(sizeof(detail::cpp_signature_tag)/sizeof(char)))); + doc_len = len(func_doc); + } + + str res="\n"; + str pad = "\n"; + + if(show_py_signature) + { + str sig = pretty_signature(*fi, n_overloads,false); + res+=sig; + if(doc_len || show_cpp_signature )res+=" :"; + pad+= str(" "); + } + + if(doc_len) + { + if(show_py_signature) + res+=pad; + res+= pad.join(func_doc.split("\n")); + } + + if( show_cpp_signature) + { + if(len(res)>1) + res+="\n"+pad; + res+=detail::cpp_signature_tag+pad+" "+pretty_signature(*fi, n_overloads,true); + } + + signatures.append(res); + } + ++sfi; + n_overloads = 0; + }else + ++n_overloads ; + } + + return signatures; + } + + +}}} + diff --git a/libs/python/src/object/inheritance.cpp b/libs/python/src/object/inheritance.cpp new file mode 100644 index 000000000..7dc9db1cd --- /dev/null +++ b/libs/python/src/object/inheritance.cpp @@ -0,0 +1,495 @@ +// Copyright David Abrahams 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#include <boost/python/object/inheritance.hpp> +#include <boost/python/type_id.hpp> +#include <boost/graph/breadth_first_search.hpp> +#if _MSC_FULL_VER >= 13102171 && _MSC_FULL_VER <= 13102179 +# include <boost/graph/reverse_graph.hpp> +#endif +#include <boost/graph/adjacency_list.hpp> +#include <boost/graph/reverse_graph.hpp> +#include <boost/property_map/property_map.hpp> +#include <boost/bind.hpp> +#include <boost/integer_traits.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/tuple/tuple_comparison.hpp> +#include <queue> +#include <vector> +#include <functional> + +// +// Procedure: +// +// The search is a BFS over the space of (type,address) pairs +// guided by the edges of the casting graph whose nodes +// correspond to classes, and whose edges are traversed by +// applying associated cast functions to an address. We use +// vertex distance to the goal node in the cast_graph to rate the +// paths. The vertex distance to any goal node is calculated on +// demand and outdated by the addition of edges to the graph. + +namespace boost { +namespace +{ + enum edge_cast_t { edge_cast = 8010 }; + template <class T> inline void unused_variable(const T&) { } +} + +// Install properties +BOOST_INSTALL_PROPERTY(edge, cast); + +namespace +{ + typedef void*(*cast_function)(void*); + + // + // Here we put together the low-level data structures of the + // casting graph representation. + // + typedef python::type_info class_id; + + // represents a graph of available casts + +#if 0 + struct cast_graph + : +#else + typedef +#endif + adjacency_list<vecS,vecS, bidirectionalS, no_property + + // edge index property allows us to look up edges in the connectivity matrix + , property<edge_index_t,std::size_t + + // The function which casts a void* from the edge's source type + // to its destination type. + , property<edge_cast_t,cast_function> > > +#if 0 + {}; +#else + cast_graph; +#endif + + typedef cast_graph::vertex_descriptor vertex_t; + typedef cast_graph::edge_descriptor edge_t; + + struct smart_graph + { + typedef std::vector<std::size_t>::const_iterator node_distance_map; + + typedef std::pair<cast_graph::out_edge_iterator + , cast_graph::out_edge_iterator> out_edges_t; + + // Return a map of the distances from any node to the given + // target node + node_distance_map distances_to(vertex_t target) const + { + std::size_t n = num_vertices(m_topology); + if (m_distances.size() != n * n) + { + m_distances.clear(); + m_distances.resize(n * n, (std::numeric_limits<std::size_t>::max)()); + m_known_vertices = n; + } + + std::vector<std::size_t>::iterator to_target = m_distances.begin() + n * target; + + // this node hasn't been used as a target yet + if (to_target[target] != 0) + { + typedef reverse_graph<cast_graph> reverse_cast_graph; + reverse_cast_graph reverse_topology(m_topology); + + to_target[target] = 0; + + breadth_first_search( + reverse_topology, target + , visitor( + make_bfs_visitor( + record_distances( + make_iterator_property_map( + to_target + , get(vertex_index, reverse_topology) +# ifdef BOOST_NO_STD_ITERATOR_TRAITS + , *to_target +# endif + ) + , on_tree_edge() + )))); + } + + return to_target; + } + + cast_graph& topology() { return m_topology; } + cast_graph const& topology() const { return m_topology; } + + smart_graph() + : m_known_vertices(0) + {} + + private: + cast_graph m_topology; + mutable std::vector<std::size_t> m_distances; + mutable std::size_t m_known_vertices; + }; + + smart_graph& full_graph() + { + static smart_graph x; + return x; + } + + smart_graph& up_graph() + { + static smart_graph x; + return x; + } + + // + // Our index of class types + // + using boost::python::objects::dynamic_id_function; + typedef tuples::tuple< + class_id // static type + , vertex_t // corresponding vertex + , dynamic_id_function // dynamic_id if polymorphic, or 0 + > + index_entry_interface; + typedef index_entry_interface::inherited index_entry; + enum { ksrc_static_t, kvertex, kdynamic_id }; + + typedef std::vector<index_entry> type_index_t; + + + type_index_t& type_index() + { + static type_index_t x; + return x; + } + + template <class Tuple> + struct select1st + { + typedef typename tuples::element<0, Tuple>::type result_type; + + result_type const& operator()(Tuple const& x) const + { + return tuples::get<0>(x); + } + }; + + // map a type to a position in the index + inline type_index_t::iterator type_position(class_id type) + { + typedef index_entry entry; + + return std::lower_bound( + type_index().begin(), type_index().end() + , boost::make_tuple(type, vertex_t(), dynamic_id_function(0)) + , boost::bind<bool>(std::less<class_id>() + , boost::bind<class_id>(select1st<entry>(), _1) + , boost::bind<class_id>(select1st<entry>(), _2))); + } + + inline index_entry* seek_type(class_id type) + { + type_index_t::iterator p = type_position(type); + if (p == type_index().end() || tuples::get<ksrc_static_t>(*p) != type) + return 0; + else + return &*p; + } + + // Get the entry for a type, inserting if necessary + inline type_index_t::iterator demand_type(class_id type) + { + type_index_t::iterator p = type_position(type); + + if (p != type_index().end() && tuples::get<ksrc_static_t>(*p) == type) + return p; + + vertex_t v = add_vertex(full_graph().topology()); + vertex_t v2 = add_vertex(up_graph().topology()); + unused_variable(v2); + assert(v == v2); + return type_index().insert(p, boost::make_tuple(type, v, dynamic_id_function(0))); + } + + // Map a two types to a vertex in the graph, inserting if necessary + typedef std::pair<type_index_t::iterator, type_index_t::iterator> + type_index_iterator_pair; + + inline type_index_iterator_pair + demand_types(class_id t1, class_id t2) + { + // be sure there will be no reallocation + type_index().reserve(type_index().size() + 2); + type_index_t::iterator first = demand_type(t1); + type_index_t::iterator second = demand_type(t2); + if (first == second) + ++first; + return std::make_pair(first, second); + } + + struct q_elt + { + q_elt(std::size_t distance + , void* src_address + , vertex_t target + , cast_function cast + ) + : distance(distance) + , src_address(src_address) + , target(target) + , cast(cast) + {} + + std::size_t distance; + void* src_address; + vertex_t target; + cast_function cast; + + bool operator<(q_elt const& rhs) const + { + return distance < rhs.distance; + } + }; + + // Optimization: + // + // Given p, src_t, dst_t + // + // Get a pointer pd to the most-derived object + // if it's polymorphic, dynamic_cast to void* + // otherwise pd = p + // + // Get the most-derived typeid src_td + // + // ptrdiff_t offset = p - pd + // + // Now we can keep a cache, for [src_t, offset, src_td, dst_t] of + // the cast transformation function to use on p and the next src_t + // in the chain. src_td, dst_t don't change throughout this + // process. In order to represent unreachability, when a pair is + // found to be unreachable, we stick a 0-returning "dead-cast" + // function in the cache. + + // This is needed in a few places below + inline void* identity_cast(void* p) + { + return p; + } + + void* search(smart_graph const& g, void* p, vertex_t src, vertex_t dst) + { + // I think this test was thoroughly bogus -- dwa + // If we know there's no path; bail now. + // if (src > g.known_vertices() || dst > g.known_vertices()) + // return 0; + + smart_graph::node_distance_map d(g.distances_to(dst)); + + if (d[src] == (std::numeric_limits<std::size_t>::max)()) + return 0; + + typedef property_map<cast_graph,edge_cast_t>::const_type cast_map; + cast_map casts = get(edge_cast, g.topology()); + + typedef std::pair<vertex_t,void*> search_state; + typedef std::vector<search_state> visited_t; + visited_t visited; + std::priority_queue<q_elt> q; + + q.push(q_elt(d[src], p, src, identity_cast)); + while (!q.empty()) + { + q_elt top = q.top(); + q.pop(); + + // Check to see if we have a real state + void* dst_address = top.cast(top.src_address); + if (dst_address == 0) + continue; + + if (top.target == dst) + return dst_address; + + search_state s(top.target,dst_address); + + visited_t::iterator pos = std::lower_bound( + visited.begin(), visited.end(), s); + + // If already visited, continue + if (pos != visited.end() && *pos == s) + continue; + + visited.insert(pos, s); // mark it + + // expand it: + smart_graph::out_edges_t edges = out_edges(s.first, g.topology()); + for (cast_graph::out_edge_iterator p = edges.first + , finish = edges.second + ; p != finish + ; ++p + ) + { + edge_t e = *p; + q.push(q_elt( + d[target(e, g.topology())] + , dst_address + , target(e, g.topology()) + , boost::get(casts, e))); + } + } + return 0; + } + + struct cache_element + { + typedef tuples::tuple< + class_id // source static type + , class_id // target type + , std::ptrdiff_t // offset within source object + , class_id // source dynamic type + >::inherited key_type; + + cache_element(key_type const& k) + : key(k) + , offset(0) + {} + + key_type key; + std::ptrdiff_t offset; + + BOOST_STATIC_CONSTANT( + std::ptrdiff_t, not_found = integer_traits<std::ptrdiff_t>::const_min); + + bool operator<(cache_element const& rhs) const + { + return this->key < rhs.key; + } + + bool unreachable() const + { + return offset == not_found; + } + }; + + enum { kdst_t = ksrc_static_t + 1, koffset, ksrc_dynamic_t }; + typedef std::vector<cache_element> cache_t; + + cache_t& cache() + { + static cache_t x; + return x; + } + + inline void* convert_type(void* const p, class_id src_t, class_id dst_t, bool polymorphic) + { + // Quickly rule out unregistered types + index_entry* src_p = seek_type(src_t); + if (src_p == 0) + return 0; + + index_entry* dst_p = seek_type(dst_t); + if (dst_p == 0) + return 0; + + // Look up the dynamic_id function and call it to get the dynamic + // info + boost::python::objects::dynamic_id_t dynamic_id = polymorphic + ? tuples::get<kdynamic_id>(*src_p)(p) + : std::make_pair(p, src_t); + + // Look in the cache first for a quickie address translation + std::ptrdiff_t offset = (char*)p - (char*)dynamic_id.first; + + cache_element seek(boost::make_tuple(src_t, dst_t, offset, dynamic_id.second)); + cache_t& c = cache(); + cache_t::iterator const cache_pos + = std::lower_bound(c.begin(), c.end(), seek); + + + // if found in the cache, we're done + if (cache_pos != c.end() && cache_pos->key == seek.key) + { + return cache_pos->offset == cache_element::not_found + ? 0 : (char*)p + cache_pos->offset; + } + + // If we are starting at the most-derived type, only look in the up graph + smart_graph const& g = polymorphic && dynamic_id.second != src_t + ? full_graph() : up_graph(); + + void* result = search( + g, p, tuples::get<kvertex>(*src_p) + , tuples::get<kvertex>(*dst_p)); + + // update the cache + c.insert(cache_pos, seek)->offset + = (result == 0) ? cache_element::not_found : (char*)result - (char*)p; + + return result; + } +} + +namespace python { namespace objects { + +BOOST_PYTHON_DECL void* find_dynamic_type(void* p, class_id src_t, class_id dst_t) +{ + return convert_type(p, src_t, dst_t, true); +} + +BOOST_PYTHON_DECL void* find_static_type(void* p, class_id src_t, class_id dst_t) +{ + return convert_type(p, src_t, dst_t, false); +} + +BOOST_PYTHON_DECL void add_cast( + class_id src_t, class_id dst_t, cast_function cast, bool is_downcast) +{ + // adding an edge will invalidate any record of unreachability in + // the cache. + static std::size_t expected_cache_len = 0; + cache_t& c = cache(); + if (c.size() > expected_cache_len) + { + c.erase(std::remove_if( + c.begin(), c.end(), + mem_fn(&cache_element::unreachable)) + , c.end()); + + // If any new cache entries get added, we'll have to do this + // again when the next edge is added + expected_cache_len = c.size(); + } + + type_index_iterator_pair types = demand_types(src_t, dst_t); + vertex_t src = tuples::get<kvertex>(*types.first); + vertex_t dst = tuples::get<kvertex>(*types.second); + + cast_graph* const g[2] = { &up_graph().topology(), &full_graph().topology() }; + + for (cast_graph*const* p = g + (is_downcast ? 1 : 0); p < g + 2; ++p) + { + edge_t e; + bool added; + + tie(e, added) = add_edge(src, dst, **p); + assert(added); + + put(get(edge_cast, **p), e, cast); + put(get(edge_index, **p), e, num_edges(full_graph().topology()) - 1); + } +} + +BOOST_PYTHON_DECL void register_dynamic_id_aux( + class_id static_id, dynamic_id_function get_dynamic_id) +{ + tuples::get<kdynamic_id>(*demand_type(static_id)) = get_dynamic_id; +} + +}}} // namespace boost::python::objects diff --git a/libs/python/src/object/iterator.cpp b/libs/python/src/object/iterator.cpp new file mode 100644 index 000000000..3f6c4adac --- /dev/null +++ b/libs/python/src/object/iterator.cpp @@ -0,0 +1,39 @@ +// Copyright David Abrahams 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/python/object/iterator_core.hpp> +#include <boost/python/object/function_object.hpp> +#include <boost/bind.hpp> +#include <boost/mpl/vector/vector10.hpp> + +namespace boost { namespace python { namespace objects { + +namespace +{ + PyObject* identity(PyObject* args_, PyObject*) + { + PyObject* x = PyTuple_GET_ITEM(args_,0); + Py_INCREF(x); + return x; + } +} + +BOOST_PYTHON_DECL object const& identity_function() +{ + static object result( + function_object( + py_function(&identity, mpl::vector2<PyObject*,PyObject*>()) + ) + ); + return result; +} + +void stop_iteration_error() +{ + PyErr_SetObject(PyExc_StopIteration, Py_None); + throw_error_already_set(); +} + +}}} // namespace boost::python::objects diff --git a/libs/python/src/object/life_support.cpp b/libs/python/src/object/life_support.cpp new file mode 100644 index 000000000..b7e9aa861 --- /dev/null +++ b/libs/python/src/object/life_support.cpp @@ -0,0 +1,121 @@ +// Copyright David Abrahams 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#include <boost/python/object/life_support.hpp> +#include <boost/python/detail/none.hpp> +#include <boost/python/refcount.hpp> + +namespace boost { namespace python { namespace objects { + +struct life_support +{ + PyObject_HEAD + PyObject* patient; +}; + +extern "C" +{ + static void + life_support_dealloc(PyObject* self) + { + Py_XDECREF(((life_support*)self)->patient); + self->ob_type->tp_free(self); + } + + static PyObject * + life_support_call(PyObject *self, PyObject *arg, PyObject * /*kw*/) + { + // Let the patient die now + Py_XDECREF(((life_support*)self)->patient); + ((life_support*)self)->patient = 0; + // Let the weak reference die. This probably kills us. + Py_XDECREF(PyTuple_GET_ITEM(arg, 0)); + return ::boost::python::detail::none(); + } +} + +PyTypeObject life_support_type = { + PyVarObject_HEAD_INIT(NULL, 0)//(&PyType_Type) + const_cast<char*>("Boost.Python.life_support"), + sizeof(life_support), + 0, + life_support_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, //(reprfunc)func_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + life_support_call, /* tp_call */ + 0, /* tp_str */ + 0, // PyObject_GenericGetAttr, /* tp_getattro */ + 0, // PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT /* | Py_TPFLAGS_HAVE_GC */,/* tp_flags */ + 0, /* tp_doc */ + 0, // (traverseproc)func_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, //offsetof(PyLife_SupportObject, func_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, // func_memberlist, /* tp_members */ + 0, //func_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, //offsetof(PyLife_SupportObject, func_dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ +#if PYTHON_API_VERSION >= 1012 + 0 /* tp_del */ +#endif +}; + +PyObject* make_nurse_and_patient(PyObject* nurse, PyObject* patient) +{ + if (nurse == Py_None || nurse == patient) + return nurse; + + if (Py_TYPE(&life_support_type) == 0) + { + Py_TYPE(&life_support_type) = &PyType_Type; + PyType_Ready(&life_support_type); + } + + life_support* system = PyObject_New(life_support, &life_support_type); + if (!system) + return 0; + + system->patient = 0; + + // We're going to leak this reference, but don't worry; the + // life_support system decrements it when the nurse dies. + PyObject* weakref = PyWeakref_NewRef(nurse, (PyObject*)system); + + // weakref has either taken ownership, or we have to release it + // anyway + Py_DECREF(system); + if (!weakref) + return 0; + + system->patient = patient; + Py_XINCREF(patient); // hang on to the patient until death + return weakref; +} + +}}} // namespace boost::python::objects diff --git a/libs/python/src/object/pickle_support.cpp b/libs/python/src/object/pickle_support.cpp new file mode 100644 index 000000000..428c07b6c --- /dev/null +++ b/libs/python/src/object/pickle_support.cpp @@ -0,0 +1,78 @@ +// (C) Copyright R.W. Grosse-Kunstleve 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/python/make_function.hpp> +#include <boost/python/object/class.hpp> +#include <boost/python/tuple.hpp> +#include <boost/python/list.hpp> +#include <boost/python/dict.hpp> +#include <boost/python/str.hpp> + +namespace boost { namespace python { + +namespace { + + tuple instance_reduce(object instance_obj) + { + list result; + object instance_class(instance_obj.attr("__class__")); + result.append(instance_class); + object none; + if (!getattr(instance_obj, "__safe_for_unpickling__", none)) + { + str type_name(getattr(instance_class, "__name__")); + str module_name(getattr(instance_class, "__module__", object(""))); + if (module_name) + module_name += "."; + + PyErr_SetObject( + PyExc_RuntimeError, + ( "Pickling of \"%s\" instances is not enabled" + " (http://www.boost.org/libs/python/doc/v2/pickle.html)" + % (module_name+type_name)).ptr() + ); + + throw_error_already_set(); + } + object getinitargs = getattr(instance_obj, "__getinitargs__", none); + tuple initargs; + if (!getinitargs.is_none()) { + initargs = tuple(getinitargs()); + } + result.append(initargs); + object getstate = getattr(instance_obj, "__getstate__", none); + object instance_dict = getattr(instance_obj, "__dict__", none); + long len_instance_dict = 0; + if (!instance_dict.is_none()) { + len_instance_dict = len(instance_dict); + } + if (!getstate.is_none()) { + if (len_instance_dict > 0) { + object getstate_manages_dict = getattr( + instance_obj, "__getstate_manages_dict__", none); + if (getstate_manages_dict.is_none()) { + PyErr_SetString(PyExc_RuntimeError, + "Incomplete pickle support" + " (__getstate_manages_dict__ not set)"); + throw_error_already_set(); + } + } + result.append(getstate()); + } + else if (len_instance_dict > 0) { + result.append(instance_dict); + } + return tuple(result); + } + +} // namespace + +object const& make_instance_reduce_function() +{ + static object result(&instance_reduce); + return result; +} + +}} // namespace boost::python diff --git a/libs/python/src/object/stl_iterator.cpp b/libs/python/src/object/stl_iterator.cpp new file mode 100644 index 000000000..e32d32145 --- /dev/null +++ b/libs/python/src/object/stl_iterator.cpp @@ -0,0 +1,48 @@ +// Copyright Eric Niebler 2005. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Credits: +// Andreas Kl\:ockner for fixing increment() to handle +// error conditions. + +#include <boost/python/object.hpp> +#include <boost/python/handle.hpp> +#include <boost/python/object/stl_iterator_core.hpp> + +namespace boost { namespace python { namespace objects +{ + +stl_input_iterator_impl::stl_input_iterator_impl() + : it_() + , ob_() +{ +} + +stl_input_iterator_impl::stl_input_iterator_impl(boost::python::object const &ob) + : it_(ob.attr("__iter__")()) + , ob_() +{ + this->increment(); +} + +void stl_input_iterator_impl::increment() +{ + this->ob_ = boost::python::handle<>( + boost::python::allow_null(PyIter_Next(this->it_.ptr()))); + if (PyErr_Occurred()) + throw boost::python::error_already_set(); +} + +bool stl_input_iterator_impl::equal(stl_input_iterator_impl const &that) const +{ + return !this->ob_ == !that.ob_; +} + +boost::python::handle<> const &stl_input_iterator_impl::current() const +{ + return this->ob_; +} + +}}} // namespace boost::python::objects |