247 lines
8.1 KiB
C++
247 lines
8.1 KiB
C++
// 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
|