All Classes Namespaces Functions Variables Typedefs Enumerations
py2py3cppwrapper.h
1 /*
2  * Author: Markus Friedrich
3  *
4  * This file is free and unencumbered software released into the public domain.
5  *
6  * Anyone is free to copy, modify, publish, use, compile, sell, or
7  * distribute this software, either in source code form or as a compiled
8  * binary, for any purpose, commercial or non-commercial, and by any
9  * means.
10  *
11  * In jurisdictions that recognize copyright laws, the author or authors
12  * of this software dedicate any and all copyright interest in the
13  * software to the public domain. We make this dedication for the benefit
14  * of the public at large and to the detriment of our heirs and
15  * successors. We intend this dedication to be an overt act of
16  * relinquishment in perpetuity of all present and future rights to this
17  * software under copyright law.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * For more information, please refer to <http://unlicense.org/>
28  */
29 
30 #ifndef _PY2PY3CPPWRAPPER_H_
31 #define _PY2PY3CPPWRAPPER_H_
32 
33 #include <Python.h>
34 #include <string>
35 #include <vector>
36 #include <stdexcept>
37 #include <memory>
38 #include <sstream>
39 #include <memory>
40 #include <boost/locale/encoding_utf.hpp> // gcc does not support <codecvt> yet
41 
42 namespace PythonCpp {
43 
44 // initialize python giving main as program name to python
45 void initializePython(const std::string &main, const std::string &home="") {
46  static const std::string mainStatic=main;
47 #if PY_MAJOR_VERSION < 3
48  Py_SetProgramName(const_cast<char*>(mainStatic.c_str()));
49  if(!home.empty())
50  Py_SetPythonHome(const_cast<char*>(home.c_str()));
51 #else
52  Py_SetProgramName(const_cast<wchar_t*>(boost::locale::conv::utf_to_utf<wchar_t>(mainStatic).c_str()));
53  if(!home.empty())
54  Py_SetPythonHome(const_cast<wchar_t*>(boost::locale::conv::utf_to_utf<wchar_t>(home).c_str()));
55 #endif
56  Py_InitializeEx(0);
57 }
58 
59 // wrap some python 3 function to also work in python 2 (the wrappers have suffix _Py2Py2
60 // and are later defined without the suffix as a macro)
61 
62 inline bool PyLong_Check_Py2Py3(PyObject *o) {
63 #if PY_MAJOR_VERSION < 3
64  return PyLong_Check(o) || PyInt_Check(o);
65 #else
66  return PyLong_Check(o);
67 #endif
68 }
69 
70 inline long PyLong_AsLong_Py2Py3(PyObject *o) {
71 #if PY_MAJOR_VERSION < 3
72  if(PyInt_Check(o))
73  return PyInt_AsLong(o);
74  return PyLong_AsLong(o);
75 #else
76  return PyLong_AsLong(o);
77 #endif
78 }
79 
80 inline bool PyUnicode_Check_Py2Py3(PyObject *o) {
81 #if PY_MAJOR_VERSION < 3
82  return PyUnicode_Check(o) || PyString_Check(o);
83 #else
84  return PyUnicode_Check(o);
85 #endif
86 }
87 
88 // we cannot return char* here since for python 3 this would lead to a reference to a temporary
89 inline std::string PyUnicode_AsUTF8_Py2Py3(PyObject *o) {
90 #if PY_MAJOR_VERSION < 3
91  if(PyString_Check(o)) {
92  char *retc=PyString_AsString(o);
93  if(!retc) return "";
94  return retc;
95  }
96  PyObject *str=PyUnicode_AsUTF8String(o);
97  if(!str) return "";
98  char *retc=PyString_AsString(str);
99  if(!retc) {
100  Py_DECREF(str);
101  return "";
102  }
103  std::string ret=retc;
104  Py_DECREF(str);
105  return ret;
106 #else
107  char *retc=PyUnicode_AsUTF8(o);
108  if(!retc) return "";
109  return retc;
110 #endif
111 }
112 
113 // now define the wrappers without the suffix: now the wrappers are active!!!!!
114 
115 #undef PyLong_Check
116 #undef PyUnicode_Check
117 #define PyLong_Check PythonCpp::PyLong_Check_Py2Py3
118 #define PyLong_AsLong PythonCpp::PyLong_AsLong_Py2Py3
119 #define PyUnicode_Check PythonCpp::PyUnicode_Check_Py2Py3
120 #define PyUnicode_AsUTF8 PythonCpp::PyUnicode_AsUTF8_Py2Py3
121 
122 // make PyRun_String a function
123 inline PyObject* PyRun_String_func(const char *str, int start, PyObject *globals, PyObject *locals) { return PyRun_String(str, start, globals, locals); }
124 #undef PyRun_String
125 #define PyRun_String PythonCpp::PyRun_String_func
126 
127 // make PyFloat_Check a function
128 inline int PyFloat_Check_func(PyObject *p) { return PyFloat_Check(p); }
129 #undef PyFloat_Check
130 #define PyFloat_Check PythonCpp::PyFloat_Check_func
131 
132 // make PyFloat_AS_DOUBLE a function
133 inline int PyFloat_AS_DOUBLE_func(PyObject *p) { return PyFloat_AS_DOUBLE(p); }
134 #undef PyFloat_AS_DOUBLE
135 #define PyFloat_AS_DOUBLE PythonCpp::PyFloat_AS_DOUBLE_func
136 
137 // make PyBool_Check a function
138 inline int PyBool_Check_func(PyObject *p) { return PyBool_Check(p); }
139 #undef PyBool_Check
140 #define PyBool_Check PythonCpp::PyBool_Check_func
141 
142 // make PyList_Check a function
143 inline int PyList_Check_func(PyObject *p) { return PyList_Check(p); }
144 #undef PyList_Check
145 #define PyList_Check PythonCpp::PyList_Check_func
146 
147 // make PyObject_TypeCheck a function
148 inline int PyObject_TypeCheck_func(PyObject *p, PyTypeObject *type) { return PyObject_TypeCheck(p, type); }
149 #undef PyObject_TypeCheck
150 #define PyObject_TypeCheck PythonCpp::PyObject_TypeCheck_func
151 
152 // we use this for python object for c++ reference counting
153 class PyO {
154  public:
155  constexpr PyO() : p(nullptr) {}
156  explicit PyO(PyObject *src, bool srcIsBorrowedRef=false) : p(src) { if(srcIsBorrowedRef) Py_XINCREF(p); } // use srcIsBorrowedRef=true if src is a borrowed reference
157  PyO(const PyO &r) : p(r.p) { Py_XINCREF(p); }
158  PyO(PyO &&r) : p(r.p) { r.p=nullptr; }
159  ~PyO() { Py_XDECREF(p); }
160  PyO& operator=(const PyO &r) { Py_XDECREF(p); p=r.p; Py_XINCREF(p); return *this; }
161  PyO& operator=(PyO &&r) { Py_XDECREF(p); p=r.p; r.p=nullptr; return *this; }
162  void reset() { Py_XDECREF(p); p=nullptr; }
163  void reset(PyObject *src, bool srcIsBorrowedRef=false) { Py_XDECREF(p); p=src; if(srcIsBorrowedRef) Py_XINCREF(p); } // use srcIsBorrowedRef=true if src is a borrowed reference
164  void swap(PyO &r) { PyObject *temp=p; p=r.p; r.p=temp; }
165  PyObject* get(bool incRef=false) const { if(incRef) Py_XINCREF(p); return p; } // use incRef=true if the caller steals a reference of the returned PyObject
166  long use_count() const { return p ? Py_REFCNT(p) : 0; }
167  PyObject* operator->() const { return p; }
168  PyO& incRef() { Py_XINCREF(p); return *this; } // use if the caller steals a reference of the returned PyObject
169  operator bool() const { return p!=nullptr; }
170  protected:
171  PyObject *p;
172 };
173 
174 inline bool operator==(const PyO& l, const PyO& r) { return l.get()==r.get(); }
175 inline bool operator!=(const PyO& l, const PyO& r) { return l.get()!=r.get(); }
176 inline bool operator< (const PyO& l, const PyO& r) { return l.get()< r.get(); }
177 inline bool operator> (const PyO& l, const PyO& r) { return l.get()> r.get(); }
178 inline bool operator<=(const PyO& l, const PyO& r) { return l.get()<=r.get(); }
179 inline bool operator>=(const PyO& l, const PyO& r) { return l.get()>=r.get(); }
180 
181 // A Python error exception object.
182 // Stores the file and line number of the C++ file where the error occured.
183 // Stores also the Python error objects type, value and traceback.
184 class PythonException : public std::exception {
185  public:
186  PythonException(const char *file_, int line_);
187  ~PythonException() throw() {}
188  std::string getFile() { return file; }
189  int getLine() { return line; }
190  PyO getType() { return type; }
191  PyO getValue() { return value; }
192  PyO getTraceback() { return traceback; }
193  const char* what() const throw();
194  private:
195  std::string file;
196  int line;
197  PyO type, value, traceback;
198  mutable std::string msg;
199 };
200 
201 // check for a python exception and throw a PythonException if one exists
202 void checkPythonError() {
203  if(PyErr_Occurred())
204  throw PythonException("", 0);
205 }
206 
207 // helper struct to map the return type of the callPy function
208 // default: map to the same type
209 template<typename T>
210 struct MapRetType {
211  typedef T type;
212  inline static type convert(const T &r) {
213  return r;
214  }
215 };
216 // specialization: map PyObject* to PyO
217 template<> struct MapRetType<PyObject*> {
218  typedef PyO type;
219  inline static PyO convert(PyObject *r) {
220  if(!r)
221  throw std::runtime_error("Internal error: Expected python object but got NULL pointer and no python exception is set.");
222  if(Py_REFCNT(r)<=0)
223  throw std::runtime_error("Internal error: Expected python object but a python object with 0 refcount and no python exception is set.");
224  return PyO(r);
225  }
226 };
227 
228 // helper function to map the argument types of the callPy function
229 // default: map to the same type
230 template<typename Arg>
231 inline Arg convertArg(const Arg &arg) {
232  return arg;
233 }
234 // specialization:: map PyO to PyObject*
235 inline PyObject* convertArg(const PyO &o) {
236  if(o && o.use_count()<=0)
237  throw std::runtime_error("Internal error: access object with reference count <= 0. Check the source code.");
238  return o.get();
239 }
240 // specialization:: map std::string to const char*
241 inline const char* convertArg(const std::string &o) {
242  return o.c_str();
243 }
244 
245 // Call Python function func with arguments args.
246 // Use the macro CALLPY or CALLPYB, see below.
247 template<typename PyRet, typename... PyArgs, typename... CallArgs>
248 inline typename MapRetType<PyRet>::type callPy(const char *file, int line, PyRet (*func)(PyArgs...), CallArgs&&... args) {
249  PyRet ret=func(convertArg(std::forward<CallArgs>(args))...);
250  if(PyErr_Occurred())
251  throw PythonException(file, line);
252  return MapRetType<PyRet>::convert(ret);
253 }
254 
255 // Macro to call callPy(...)
256 // Use this macro to call a python function returning a new reference to a python object or any other return type.
257 // Note, if the python function steals a reference of any of this arguments you have to call arg.incRef() on
258 // each such arguments.
259 #define CALLPY(...) PythonCpp::callPy(__FILE__, __LINE__, __VA_ARGS__)
260 
261 // Macro to call callPy(...).incRef()
262 // Use this macro to call a python function returning a borrowed reference to a python object.
263 // Note, if the python function steals a reference of any of this arguments you have to call arg.incRef() on
264 // each such arguments.
265 #define CALLPYB(...) PythonCpp::callPy(__FILE__, __LINE__, __VA_ARGS__).incRef()
266 
267 // c++ PythonException exception with the content of a python exception
268 PythonException::PythonException(const char *file_, int line_) : file(file_), line(line_) {
269  // fetch if error has occured
270  if(!PyErr_Occurred())
271  throw std::runtime_error("Internal error: PythonException object created but no python error occured.");
272  // fetch error objects and save objects
273  PyObject *type_, *value_, *traceback_;
274  PyErr_Fetch(&type_, &value_, &traceback_);
275  type=PyO(type_);
276  value=PyO(value_);
277  traceback=PyO(traceback_);
278 }
279 
280 const char* PythonException::what() const throw() {
281  if(!msg.empty())
282  return msg.c_str();
283 
284  // redirect stderr
285  PyObject *savedstderr=PySys_GetObject(const_cast<char*>("stderr"));
286  if(!savedstderr)
287  throw std::runtime_error("Internal error: no sys.stderr available.");
288  PyObject *io=PyImport_ImportModule("io");
289  if(!io)
290  throw std::runtime_error("Internal error: cannot load io module.");
291 #if PY_MAJOR_VERSION < 3
292  PyObject *fileIO=PyObject_GetAttrString(io, "BytesIO"); // sys.stderr is a file is bytes mode
293 #else
294  PyObject *fileIO=PyObject_GetAttrString(io, "StringIO"); // sys.stderr is a file in text mode
295 #endif
296  if(!fileIO)
297  throw std::runtime_error("Internal error: cannot get in memory file class.");
298  Py_DECREF(io);
299  PyObject *buf=PyObject_CallObject(fileIO, NULL);
300  Py_DECREF(fileIO);
301  if(!buf)
302  throw std::runtime_error("Internal error: cannot create new in memory file instance");
303  if(PySys_SetObject(const_cast<char*>("stderr"), buf)!=0)
304  throw std::runtime_error("Internal error: cannot redirect stderr");
305  // restore error
306  PyErr_Restore(type.get(), value.get(), traceback.get());
307  Py_XINCREF(type.get());
308  Py_XINCREF(value.get());
309  Py_XINCREF(traceback.get());
310  // print to redirected stderr
311  PyErr_Print();
312  // unredirect stderr
313  if(PySys_SetObject(const_cast<char*>("stderr"), savedstderr)!=0)
314  throw std::runtime_error("Internal error: cannot revert redirect stderr");
315  // get redirected output as string
316  PyObject *getvalue=PyObject_GetAttrString(buf, "getvalue");
317  if(!getvalue)
318  throw std::runtime_error("Internal error: cannot get getvalue attribute");
319  PyObject *pybufstr=PyObject_CallObject(getvalue, NULL);
320  if(!pybufstr)
321  throw std::runtime_error("Internal error: cannot get string from in memory file output");
322  Py_DECREF(getvalue);
323  Py_DECREF(buf);
324 #if PY_MAJOR_VERSION < 3
325  char *strc=PyBytes_AsString(pybufstr); // sys.stderr is a file in bytes mode
326  if(!strc)
327  throw std::runtime_error("Internal error: cannot get c string");
328  std::string str(strc);
329 #else
330  std::string str=PyUnicode_AsUTF8(pybufstr); // sys.stderr is a file in text mode
331  if(PyErr_Occurred())
332  throw std::runtime_error("Internal error: cannot get c string");
333 #endif
334  Py_DECREF(pybufstr);
335  std::stringstream strstr;
336  strstr<<"Python exception";
337  if(!file.empty())
338  strstr<<" at "<<file<<":"<<line;
339  strstr<<":"<<std::endl<<str;
340  msg=strstr.str();
341  return msg.c_str();
342 }
343 
344 }
345 
346 #endif
Definition: py2py3cppwrapper.h:153
Definition: py2py3cppwrapper.h:184
Definition: py2py3cppwrapper.h:210

Impressum / Disclaimer / Datenschutz Generated by doxygen 1.8.5 Valid HTML