Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPEnum.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPEnum.h"
6#include "PyStrings.h"
7#include "TypeManip.h"
8#include "Utility.h"
9#include <unordered_map>
10
11//- private helpers ----------------------------------------------------------
12static PyObject* pytype_from_enum_type(const std::string& enum_type) {
13 if (enum_type == "char")
14 return (PyObject*)&PyUnicode_Type;
15 else if (enum_type == "bool")
16 return (PyObject*)&PyLong_Type; // can't use PyBool_Type as base
17 else if (strstr("long", enum_type.c_str()))
18 return (PyObject*)&PyLong_Type;
19 return (PyObject*)&PyLong_Type; // covers most cases
20}
21
22//----------------------------------------------------------------------------
23PyObject* cpyrt::pyval_from_enum(const std::string& enum_type, PyObject* pytype,
24 PyObject* btype,
25 interop::TCppScope_t enum_constant) {
26 long long llval = interop::GetEnumDataValue(enum_constant);
27
28 if (enum_type == "bool" && !(pytype && btype)) {
29 // no enum class to instantiate; the singletons can not carry attributes
30 PyObject* result = (bool)llval ? Py_True : Py_False;
31 Py_INCREF(result);
32 return result;
33 }
34
35 PyObject* bval;
36 if (enum_type == "char") {
37 char val = (char)llval;
38 bval = PyUnicode_FromOrdinal((int)val);
39 } else if (enum_type == "int" || enum_type == "unsigned int")
40 bval = PyLong_FromLong((long)llval);
41 else
42 bval = PyLong_FromLongLong(llval);
43
44 if (!bval)
45 return nullptr; // e.g. when out of range for small integers
46
47 if (pytype && btype) {
48 PyObject* args = PyTuple_New(1);
49 PyTuple_SET_ITEM(args, 0, bval);
50 bval = ((PyTypeObject*)btype)->tp_new((PyTypeObject*)pytype, args, nullptr);
51 Py_DECREF(args);
52 }
53 return bval;
54}
55
56//- enum methods -------------------------------------------------------------
57static int enum_setattro(PyObject* /* pyclass */, PyObject* /* pyname */,
58 PyObject* /* pyval */) {
59 // Helper to make enums read-only.
60 PyErr_SetString(PyExc_TypeError, "enum values are read-only");
61 return -1;
62}
63
64//----------------------------------------------------------------------------
65static PyObject* enum_repr(PyObject* self) {
66 using namespace cppjit::cpyrt;
67
68 PyObject* kls_scope =
69 PyObject_GetAttr((PyObject*)Py_TYPE(self), PyStrings::gThisModule);
70 if (!kls_scope)
71 PyErr_Clear();
72 PyObject* kls_cppname =
73 PyObject_GetAttr((PyObject*)Py_TYPE(self), PyStrings::gCppName);
74 if (!kls_cppname)
75 PyErr_Clear();
76 PyObject* obj_cppname = PyObject_GetAttr(self, PyStrings::gCppName);
77 if (!obj_cppname)
78 PyErr_Clear();
79 PyObject* obj_str = Py_TYPE(self)->tp_str(self);
80
81 PyObject* repr = nullptr;
82 if (kls_cppname && obj_cppname && obj_str) {
83 const std::string resolved =
84 interop::ResolveEnum(PyLong_AsVoidPtr(kls_scope));
85 repr = PyUnicode_FromFormat("(%s::%s) : (%s) %s",
86 PyUnicode_AsUTF8(kls_cppname),
87 PyUnicode_AsUTF8(obj_cppname), resolved.c_str(),
88 PyUnicode_AsUTF8(obj_str));
89 }
90 Py_XDECREF(obj_cppname);
91 Py_XDECREF(kls_cppname);
92
93 if (repr) {
94 Py_DECREF(obj_str);
95 return repr;
96 }
97
98 return obj_str;
99}
100
101//----------------------------------------------------------------------------
102// TODO: factor the following lookup with similar codes in Convertes and
103// TemplateProxy.cxx
104
105static std::unordered_map<std::string, std::string> gCTypesNames = {
106 {"bool", "c_bool"},
107 {"char", "c_char"},
108 {"wchar_t", "c_wchar"},
109 {"std::byte", "c_byte"},
110 {"int8_t", "c_byte"},
111 {"uint8_t", "c_ubyte"},
112 {"short", "c_short"},
113 {"int16_t", "c_int16"},
114 {"unsigned short", "c_ushort"},
115 {"uint16_t", "c_uint16"},
116 {"int", "c_int"},
117 {"unsigned int", "c_uint"},
118 {"long", "c_long"},
119 {"unsigned long", "c_ulong"},
120 {"long long", "c_longlong"},
121 {"unsigned long long", "c_ulonglong"}};
122
123// Both GetCTypesType and GetCTypesPtrType, rely on the ctypes module itself
124// caching the types (thus also making them unique), so no ref-count is needed.
125// Further, by keeping a ref-count on the module, it won't be off-loaded until
126// the 2nd cleanup cycle.
127static PyTypeObject* GetCTypesType(const std::string& cppname) {
128 static PyObject* ctmod = PyImport_ImportModule("ctypes"); // ref-count kept
129 if (!ctmod)
130 return nullptr;
131
132 auto nn = gCTypesNames.find(cppname);
133 if (nn == gCTypesNames.end()) {
134 PyErr_Format(PyExc_TypeError, "Can not find ctypes type for \"%s\"",
135 cppname.c_str());
136 return nullptr;
137 }
138
139 return (PyTypeObject*)PyObject_GetAttrString(ctmod, nn->second.c_str());
140}
141
142static PyObject* enum_ctype(PyObject* cls, PyObject* args, PyObject* kwds) {
143 PyObject* pyres = PyObject_GetAttr(cls, cpyrt::PyStrings::gUnderlying);
144 if (!pyres)
145 PyErr_Clear();
146
147 std::string underlying = pyres ? PyUnicode_AsUTF8(pyres) : "int";
148 PyTypeObject* ct = GetCTypesType(underlying);
149 if (!ct)
150 return nullptr;
151
152 return PyType_Type.tp_call((PyObject*)ct, args, kwds);
153}
154
155//- creation -----------------------------------------------------------------
157 interop::TCppScope_t scope) {
158 // Create a new enum type based on the actual C++ type. Enum values are added
159 // to the type but may also live in the enclosing scope.
160
161 CPPEnum* pyenum = nullptr;
162
163 interop::TCppScope_t etype = scope;
164 const std::string& ename = interop::GetScopedFinalName(scope);
165 if (etype) {
166 // create new enum type with labeled values in place, with a meta-class
167 // to make sure the enum values are read-only
168 const std::string& resolved = interop::ResolveEnum(etype);
169 PyObject* pyside_type = pytype_from_enum_type(resolved);
170 PyObject* pymetabases = PyTuple_New(1);
171 PyObject* btype = (PyObject*)Py_TYPE(pyside_type);
172 Py_INCREF(btype);
173 PyTuple_SET_ITEM(pymetabases, 0, btype);
174
175 PyObject* args =
176 Py_BuildValue((char*)"sO{}", (name + "_meta").c_str(), pymetabases);
177 Py_DECREF(pymetabases);
178 PyObject* pymeta = PyType_Type.tp_new(Py_TYPE(pyside_type), args, nullptr);
179 Py_DECREF(args);
180
181 // prepare the base class
182 PyObject* pybases = PyTuple_New(1);
183 Py_INCREF(pyside_type);
184 PyTuple_SET_ITEM(pybases, 0, (PyObject*)pyside_type);
185
186 // create the __cpp_name__ for templates
187 PyObject* dct = PyDict_New();
188 PyObject* pycppname = PyUnicode_FromString(ename.c_str());
189 PyObject* pycppscope = PyLong_FromVoidPtr(etype.data);
190 PyDict_SetItem(dct, PyStrings::gCppName, pycppname);
191 PyDict_SetItem(dct, PyStrings::gThisModule, pycppscope);
192 Py_DECREF(pycppname);
193 PyObject* pyresolved = PyUnicode_FromString(resolved.c_str());
194 PyDict_SetItem(dct, PyStrings::gUnderlying, pyresolved);
195 Py_DECREF(pyresolved);
196
197 // add the __module__ to allow pickling
198 std::string modname = TypeManip::extract_namespace(ename);
199 TypeManip::cppscope_to_pyscope(modname); // :: -> .
200 if (!modname.empty())
201 modname = "." + modname;
202 PyObject* pymodname =
203 PyUnicode_FromString(("cppjit.gbl" + modname).c_str());
204 PyDict_SetItem(dct, PyStrings::gModule, pymodname);
205 Py_DECREF(pymodname);
206
207 // create the actual enum class
208 args = Py_BuildValue((char*)"sOO", name.c_str(), pybases, dct);
209 Py_DECREF(pybases);
210 Py_DECREF(dct);
211 pyenum =
212 ((PyTypeObject*)pymeta)->tp_new((PyTypeObject*)pymeta, args, nullptr);
213
214 // add pythonizations
215 Utility::AddToClass((PyObject*)Py_TYPE(pyenum), "__ctype__",
216 (PyCFunction)enum_ctype, METH_VARARGS | METH_KEYWORDS);
217 ((PyTypeObject*)pyenum)->tp_repr = enum_repr;
218 ((PyTypeObject*)pyenum)->tp_str = ((PyTypeObject*)pyside_type)->tp_repr;
219
220 // collect the enum values
221 std::vector<interop::TCppScope_t> econstants =
223 bool values_ok = true;
224 for (auto econstant : econstants) {
225 PyObject* val = pyval_from_enum(resolved, pyenum, pyside_type, econstant);
226 if (!val) {
227 values_ok = false;
228 break;
229 }
230 const std::string& dname = interop::GetFinalName(econstant);
231 PyObject* pydname = PyUnicode_FromString(dname.c_str());
232 PyObject_SetAttr(pyenum, pydname, val);
233 Py_DECREF(pydname);
234 PyObject* pydcppname = PyUnicode_FromString(
235 (ename.empty() ? dname : (ename + "::" + dname)).c_str());
236 PyObject_SetAttr(val, PyStrings::gCppName, pydcppname);
237 Py_DECREF(pydcppname);
238 Py_DECREF(val);
239 }
240
241 // disable writing onto enum values
242 ((PyTypeObject*)pymeta)->tp_setattro = enum_setattro;
243
244 // final cleanup
245 Py_DECREF(args);
246 Py_DECREF(pymeta);
247
248 if (!values_ok) {
249 if (!PyErr_Occurred())
250 PyErr_SetString(PyExc_ValueError,
251 "could not set some of the enum values");
252 Py_DECREF(pyenum);
253 return nullptr;
254 }
255
256 } else {
257 // presumably not a class enum; simply pretend int
258 Py_INCREF(&PyLong_Type);
259 pyenum = (PyObject*)&PyLong_Type;
260 }
261
262 return pyenum;
263}
static PyObject * enum_repr(PyObject *self)
Definition CPPEnum.cxx:65
static PyObject * pytype_from_enum_type(const std::string &enum_type)
Definition CPPEnum.cxx:12
static PyObject * enum_ctype(PyObject *cls, PyObject *args, PyObject *kwds)
Definition CPPEnum.cxx:142
static std::unordered_map< std::string, std::string > gCTypesNames
Definition CPPEnum.cxx:105
static PyTypeObject * GetCTypesType(const std::string &cppname)
Definition CPPEnum.cxx:127
static int enum_setattro(PyObject *, PyObject *, PyObject *)
Definition CPPEnum.cxx:57
_object PyObject
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
char name[80]
Definition TGX11.cxx:142
PyObject * pyval_from_enum(const std::string &enum_type, PyObject *pytype, PyObject *btype, interop::TCppScope_t enum_constant)
Definition CPPEnum.cxx:23
PyObject CPPEnum
Definition CPPEnum.h:8
CPPEnum * CPPEnum_New(const std::string &name, interop::TCppScope_t scope)
Definition CPPEnum.cxx:156
long long GetEnumDataValue(TCppScope_t scope)
std::vector< TCppScope_t > GetEnumConstants(TCppScope_t scope)
std::string ResolveEnum(TCppScope_t enum_scope)
std::string GetFinalName(TCppScope_t type)
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
Definition API.cxx:34