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