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