// 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 #include #include #include #include #include #include #include #include #include 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("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("__module__"))); PyObject *mod = PyObject_GetAttrString( self_, "__module__"); enum_object* self = downcast(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(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("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::registry::lookup(id)); converters.m_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()); dict names_dict = extract(this->attr("names"))(); names_dict[x.attr("name")] = x; } void enum_base::export_values() { dict d = extract(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(type.attr("values"))(); object v = d.get(x, object()); return incref( (v == object() ? type(x) : v).ptr()); } }}} // namespace boost::python::object