]>
Commit | Line | Data |
---|---|---|
1 | // Copyright David Abrahams 2002. | |
2 | // Distributed under the Boost Software License, Version 1.0. (See | |
3 | // accompanying file LICENSE_1_0.txt or copy at | |
4 | // http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | #include <boost/python/object/enum_base.hpp> | |
7 | #include <boost/python/cast.hpp> | |
8 | #include <boost/python/scope.hpp> | |
9 | #include <boost/python/object.hpp> | |
10 | #include <boost/python/tuple.hpp> | |
11 | #include <boost/python/dict.hpp> | |
12 | #include <boost/python/str.hpp> | |
13 | #include <boost/python/extract.hpp> | |
14 | #include <boost/python/object_protocol.hpp> | |
15 | #include <structmember.h> | |
16 | ||
17 | namespace boost { namespace python { namespace objects { | |
18 | ||
19 | struct enum_object | |
20 | { | |
21 | #if PY_VERSION_HEX >= 0x03000000 | |
22 | PyLongObject base_object; | |
23 | #else | |
24 | PyIntObject base_object; | |
25 | #endif | |
26 | PyObject* name; | |
27 | }; | |
28 | ||
29 | static PyMemberDef enum_members[] = { | |
30 | {const_cast<char*>("name"), T_OBJECT_EX, offsetof(enum_object,name),READONLY, 0}, | |
31 | {0, 0, 0, 0, 0} | |
32 | }; | |
33 | ||
34 | ||
35 | extern "C" | |
36 | { | |
37 | static void | |
38 | enum_dealloc(enum_object* self) | |
39 | { | |
40 | Py_XDECREF(self->name); | |
41 | Py_TYPE(self)->tp_free((PyObject*)self); | |
42 | } | |
43 | ||
44 | static PyObject* enum_repr(PyObject* self_) | |
45 | { | |
46 | PyObject *mod = PyObject_GetAttrString( self_, "__module__"); | |
47 | object auto_free(handle<>(mod)); | |
48 | enum_object* self = downcast<enum_object>(self_); | |
49 | if (!self->name) | |
50 | { | |
51 | return | |
52 | #if PY_VERSION_HEX >= 0x03000000 | |
53 | PyUnicode_FromFormat("%S.%s(%ld)", mod, self_->ob_type->tp_name, PyLong_AsLong(self_)); | |
54 | #else | |
55 | PyString_FromFormat("%s.%s(%ld)", PyString_AsString(mod), self_->ob_type->tp_name, PyInt_AS_LONG(self_)); | |
56 | #endif | |
57 | } | |
58 | else | |
59 | { | |
60 | PyObject* name = self->name; | |
61 | if (name == 0) | |
62 | return 0; | |
63 | ||
64 | return | |
65 | #if PY_VERSION_HEX >= 0x03000000 | |
66 | PyUnicode_FromFormat("%S.%s.%S", mod, self_->ob_type->tp_name, name); | |
67 | #else | |
68 | PyString_FromFormat("%s.%s.%s", | |
69 | PyString_AsString(mod), self_->ob_type->tp_name, PyString_AsString(name)); | |
70 | #endif | |
71 | } | |
72 | } | |
73 | ||
74 | static PyObject* enum_str(PyObject* self_) | |
75 | { | |
76 | enum_object* self = downcast<enum_object>(self_); | |
77 | if (!self->name) | |
78 | { | |
79 | #if PY_VERSION_HEX >= 0x03000000 | |
80 | return PyLong_Type.tp_str(self_); | |
81 | #else | |
82 | return PyInt_Type.tp_str(self_); | |
83 | #endif | |
84 | } | |
85 | else | |
86 | { | |
87 | return incref(self->name); | |
88 | } | |
89 | } | |
90 | } | |
91 | ||
92 | static PyTypeObject enum_type_object = { | |
93 | PyVarObject_HEAD_INIT(NULL, 0) // &PyType_Type | |
94 | const_cast<char*>("Boost.Python.enum"), | |
95 | sizeof(enum_object), /* tp_basicsize */ | |
96 | 0, /* tp_itemsize */ | |
97 | (destructor) enum_dealloc, /* tp_dealloc */ | |
98 | 0, /* tp_print */ | |
99 | 0, /* tp_getattr */ | |
100 | 0, /* tp_setattr */ | |
101 | 0, /* tp_compare */ | |
102 | enum_repr, /* tp_repr */ | |
103 | 0, /* tp_as_number */ | |
104 | 0, /* tp_as_sequence */ | |
105 | 0, /* tp_as_mapping */ | |
106 | 0, /* tp_hash */ | |
107 | 0, /* tp_call */ | |
108 | enum_str, /* tp_str */ | |
109 | 0, /* tp_getattro */ | |
110 | 0, /* tp_setattro */ | |
111 | 0, /* tp_as_buffer */ | |
112 | Py_TPFLAGS_DEFAULT | |
113 | #if PY_VERSION_HEX < 0x03000000 | |
114 | | Py_TPFLAGS_CHECKTYPES | |
115 | #endif | |
116 | | Py_TPFLAGS_HAVE_GC | |
117 | | Py_TPFLAGS_BASETYPE, /* tp_flags */ | |
118 | 0, /* tp_doc */ | |
119 | 0, /* tp_traverse */ | |
120 | 0, /* tp_clear */ | |
121 | 0, /* tp_richcompare */ | |
122 | 0, /* tp_weaklistoffset */ | |
123 | 0, /* tp_iter */ | |
124 | 0, /* tp_iternext */ | |
125 | 0, /* tp_methods */ | |
126 | enum_members, /* tp_members */ | |
127 | 0, /* tp_getset */ | |
128 | 0, //&PyInt_Type, /* tp_base */ | |
129 | 0, /* tp_dict */ | |
130 | 0, /* tp_descr_get */ | |
131 | 0, /* tp_descr_set */ | |
132 | 0, /* tp_dictoffset */ | |
133 | 0, /* tp_init */ | |
134 | 0, /* tp_alloc */ | |
135 | 0, /* tp_new */ | |
136 | 0, /* tp_free */ | |
137 | 0, /* tp_is_gc */ | |
138 | 0, /* tp_bases */ | |
139 | 0, /* tp_mro */ | |
140 | 0, /* tp_cache */ | |
141 | 0, /* tp_subclasses */ | |
142 | 0, /* tp_weaklist */ | |
143 | #if PYTHON_API_VERSION >= 1012 | |
144 | 0 /* tp_del */ | |
145 | #endif | |
146 | }; | |
147 | ||
148 | object module_prefix(); | |
149 | ||
150 | namespace | |
151 | { | |
152 | object new_enum_type(char const* name, char const *doc) | |
153 | { | |
154 | if (enum_type_object.tp_dict == 0) | |
155 | { | |
156 | Py_TYPE(&enum_type_object) = incref(&PyType_Type); | |
157 | #if PY_VERSION_HEX >= 0x03000000 | |
158 | enum_type_object.tp_base = &PyLong_Type; | |
159 | #else | |
160 | enum_type_object.tp_base = &PyInt_Type; | |
161 | #endif | |
162 | if (PyType_Ready(&enum_type_object)) | |
163 | throw_error_already_set(); | |
164 | } | |
165 | ||
166 | type_handle metatype(borrowed(&PyType_Type)); | |
167 | type_handle base(borrowed(&enum_type_object)); | |
168 | ||
169 | // suppress the instance __dict__ in these enum objects. There | |
170 | // may be a slicker way, but this'll do for now. | |
171 | dict d; | |
172 | d["__slots__"] = tuple(); | |
173 | d["values"] = dict(); | |
174 | d["names"] = dict(); | |
175 | ||
176 | object module_name = module_prefix(); | |
177 | if (module_name) | |
178 | d["__module__"] = module_name; | |
179 | if (doc) | |
180 | d["__doc__"] = doc; | |
181 | ||
182 | object result = (object(metatype))(name, make_tuple(base), d); | |
183 | ||
184 | scope().attr(name) = result; | |
185 | ||
186 | return result; | |
187 | } | |
188 | } | |
189 | ||
190 | enum_base::enum_base( | |
191 | char const* name | |
192 | , converter::to_python_function_t to_python | |
193 | , converter::convertible_function convertible | |
194 | , converter::constructor_function construct | |
195 | , type_info id | |
196 | , char const *doc | |
197 | ) | |
198 | : object(new_enum_type(name, doc)) | |
199 | { | |
200 | converter::registration& converters | |
201 | = const_cast<converter::registration&>( | |
202 | converter::registry::lookup(id)); | |
203 | ||
204 | converters.m_class_object = downcast<PyTypeObject>(this->ptr()); | |
205 | converter::registry::insert(to_python, id); | |
206 | converter::registry::insert(convertible, construct, id); | |
207 | } | |
208 | ||
209 | void enum_base::add_value(char const* name_, long value) | |
210 | { | |
211 | // Convert name to Python string | |
212 | object name(name_); | |
213 | ||
214 | // Create a new enum instance by calling the class with a value | |
215 | object x = (*this)(value); | |
216 | ||
217 | // Store the object in the enum class | |
218 | (*this).attr(name_) = x; | |
219 | ||
220 | dict d = extract<dict>(this->attr("values"))(); | |
221 | d[value] = x; | |
222 | ||
223 | // Set the name field in the new enum instanec | |
224 | enum_object* p = downcast<enum_object>(x.ptr()); | |
225 | Py_XDECREF(p->name); | |
226 | p->name = incref(name.ptr()); | |
227 | ||
228 | dict names_dict = extract<dict>(this->attr("names"))(); | |
229 | names_dict[x.attr("name")] = x; | |
230 | } | |
231 | ||
232 | void enum_base::export_values() | |
233 | { | |
234 | dict d = extract<dict>(this->attr("names"))(); | |
235 | list items = d.items(); | |
236 | scope current; | |
237 | ||
238 | for (unsigned i = 0, max = len(items); i < max; ++i) | |
239 | api::setattr(current, items[i][0], items[i][1]); | |
240 | } | |
241 | ||
242 | PyObject* enum_base::to_python(PyTypeObject* type_, long x) | |
243 | { | |
244 | object type((type_handle(borrowed(type_)))); | |
245 | ||
246 | dict d = extract<dict>(type.attr("values"))(); | |
247 | object v = d.get(x, object()); | |
248 | return incref( | |
249 | (v == object() ? type(x) : v).ptr()); | |
250 | } | |
251 | ||
252 | }}} // namespace boost::python::object |