diff --git a/src/object/enum.cpp b/src/object/enum.cpp new file mode 100644 index 00000000..36d49801 --- /dev/null +++ b/src/object/enum.cpp @@ -0,0 +1,206 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace python { namespace objects { + +struct enum_object +{ + PyIntObject base_object; + PyObject* name; +}; + +static PyMemberDef enum_members[] = { + {"name", T_OBJECT_EX, offsetof(enum_object,name),READONLY}, + {0} +}; + + +extern "C" { + static int + enum_print(PyObject *v, std::FILE *fp, int flags) + { + PyObject* s + = (flags & Py_PRINT_RAW) ? v->ob_type->tp_str(v) : v->ob_type->tp_repr(v); + if (s == 0) + return -1; + + char const* text = PyString_AsString(s); + if (text == 0) + return -1; + + std::fprintf(fp, text); + return 0; + } + + /* flags -- not used but required by interface */ + static PyObject* enum_repr(PyObject* self_) + { + enum_object* self = downcast(self_); + if (!self->name) + { + return PyString_FromFormat("%s(%ld)", self_->ob_type->tp_name, PyInt_AS_LONG(self_)); + } + else + { + char* name = PyString_AsString(self->name); + if (name == 0) + return 0; + + return PyString_FromFormat("%s.%s", self_->ob_type->tp_name, name); + } + } + + static PyObject* enum_str(PyObject* self_) + { + enum_object* self = downcast(self_); + if (!self->name) + { + return PyInt_Type.tp_str(self_); + } + else + { + return incref(self->name); + } + } +} + +static PyTypeObject enum_type_object = { + PyObject_HEAD_INIT(0) // &PyType_Type + 0, + "Boost.Python.enum", + sizeof(enum_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + enum_print, /* 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 + | Py_TPFLAGS_CHECKTYPES + | 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 */ +}; + +object module_prefix(); + +namespace +{ + object new_enum_type(char const* name) + { + if (enum_type_object.tp_dict == 0) + { + enum_type_object.ob_type = incref(&PyType_Type); + enum_type_object.tp_base = &PyInt_Type; + 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(); + + object result = (object(metatype))( + module_prefix() + 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 + ) + : object(new_enum_type(name)) +{ + converter::registration& converters + = const_cast( + converter::registry::lookup(id)); + + converters.class_object = downcast(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(this->attr("values"))(); + d[value] = x; + + // Set the name field in the new enum instanec + enum_object* p = downcast(x.ptr()); + Py_XDECREF(p->name); + p->name = incref(name.ptr()); +} + +PyObject* enum_base::to_python(PyTypeObject* type_, long x) +{ + object type(type_handle(borrowed(type_))); + + dict d = extract(type.attr("values"))(); + object v = d.get(x, object()); + return incref( + (v == object() ? type(x) : v).ptr()); +} + +}}} // namespace boost::python::object