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