mbxmlutils  1.3.0
Multi-Body XML Utils
pycppwrapper.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 _MBXMLUTILS_PYCPPWRAPPER_H_
31#define _MBXMLUTILS_PYCPPWRAPPER_H_
32
33#include <Python.h>
34#include <string>
35#include <vector>
36#include <stdexcept>
37#include <boost/filesystem/path.hpp>
38#if PY_MAJOR_VERSION < 3
39 #error "This file can only handle python >= 3"
40#endif
41
42namespace PythonCpp {
43
44// make PyRun_String a function
45inline PyObject* PyRun_String_func(const char *str, int start, PyObject *globals, PyObject *locals) { return PyRun_String(str, start, globals, locals); }
46#undef PyRun_String
47#define PyRun_String PythonCpp::PyRun_String_func
48
49// make PyFloat_Check a function
50inline int PyFloat_Check_func(PyObject *p) { return PyFloat_Check(p); }
51#undef PyFloat_Check
52#define PyFloat_Check PythonCpp::PyFloat_Check_func
53
54// make PyFloat_AS_DOUBLE a function
55inline int PyFloat_AS_DOUBLE_func(PyObject *p) { return PyFloat_AS_DOUBLE(p); }
56#undef PyFloat_AS_DOUBLE
57#define PyFloat_AS_DOUBLE PythonCpp::PyFloat_AS_DOUBLE_func
58
59// make PyBool_Check a function
60inline int PyBool_Check_func(PyObject *p) { return PyBool_Check(p); }
61#undef PyBool_Check
62#define PyBool_Check PythonCpp::PyBool_Check_func
63
64// make PyList_Check a function
65inline int PyList_Check_func(PyObject *p) { return PyList_Check(p); }
66#undef PyList_Check
67#define PyList_Check PythonCpp::PyList_Check_func
68
69// make PyObject_TypeCheck a function
70inline int PyObject_TypeCheck_func(PyObject *p, PyTypeObject *type) { return PyObject_TypeCheck(p, type); }
71#undef PyObject_TypeCheck
72#define PyObject_TypeCheck PythonCpp::PyObject_TypeCheck_func
73
74// make PyLong_Check a function
75inline int PyLong_Check_func(PyObject *p) { return PyLong_Check(p); }
76#undef PyLong_Check
77#define PyLong_Check PythonCpp::PyLong_Check_func
78
79// make PyUnicode_Check a function
80inline int PyUnicode_Check_func(PyObject *p) { return PyUnicode_Check(p); }
81#undef PyUnicode_Check
82#define PyUnicode_Check PythonCpp::PyUnicode_Check_func
83
84// create a valid GIL in the ctor and release it again in the dtor
85class GilState {
86 public:
87 GilState() { state=PyGILState_Ensure(); }
88 ~GilState() { PyGILState_Release(state); }
89 GilState(const GilState &) = delete;
90 GilState(GilState &&) = delete;
91 GilState& operator=(const GilState &) = delete;
92 GilState& operator=(GilState &&) = delete;
93 private:
94 PyGILState_STATE state;
95};
96
97// we use this for python object for c++ reference counting
98class PyO {
99 public:
100 constexpr PyO() = default;
101 explicit PyO(PyObject *src, bool srcIsBorrowedRef=false) : p(src) { if(srcIsBorrowedRef) Py_XINCREF(p); } // use srcIsBorrowedRef=true if src is a borrowed reference
102 PyO(const PyO &r) : p(r.p) { Py_XINCREF(p); }
103 PyO(PyO &&r) noexcept : p(r.p) { r.p=nullptr; }
104 ~PyO() {
105 if(!p) return;
106 // the dtor may be called outside of a valid Python GIL -> create one
107 GilState gil;
108 Py_XDECREF(p);
109 }
110 PyO& operator=(const PyO &r) { Py_XDECREF(p); p=r.p; Py_XINCREF(p); return *this; }
111 PyO& operator=(PyO &&r) noexcept { Py_XDECREF(p); p=r.p; r.p=nullptr; return *this; }
112 void reset() { Py_XDECREF(p); p=nullptr; }
113 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
114 void swap(PyO &r) { PyObject *temp=p; p=r.p; r.p=temp; }
115 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
116 long use_count() const { return p ? Py_REFCNT(p) : 0; }
117 PyObject* operator->() const { return p; }
118 PyO& incRef() { Py_XINCREF(p); return *this; } // use if the caller steals a reference of the returned PyObject
119 operator bool() const { return p!=nullptr; }
120 protected:
121 PyObject *p{nullptr};
122};
123
124inline bool operator==(const PyO& l, const PyO& r) { return l.get()==r.get(); }
125inline bool operator!=(const PyO& l, const PyO& r) { return l.get()!=r.get(); }
126inline bool operator< (const PyO& l, const PyO& r) { return l.get()< r.get(); }
127inline bool operator> (const PyO& l, const PyO& r) { return l.get()> r.get(); }
128inline bool operator<=(const PyO& l, const PyO& r) { return l.get()<=r.get(); }
129inline bool operator>=(const PyO& l, const PyO& r) { return l.get()>=r.get(); }
130
131// A Python error exception object.
132// Stores the file and line number of the C++ file where the error occured.
133// Stores also the Python error objects type, value and traceback.
134class PythonException : public std::exception {
135 public:
136 PythonException(const char *file_, int line_);
137 virtual ~PythonException() noexcept override = default;
138 std::string getFile() { return file; }
139 int getLine() { return line; }
140 PyO getType() { return type; }
141 PyO getValue() { return value; }
142 PyO getTraceback() { return traceback; }
143 const char* what() const noexcept override;
144 private:
145 std::string file;
146 int line;
147 PyO type, value, traceback;
148 mutable std::string msg;
149};
150
151// check for a python exception and throw a PythonException if one exists
152void checkPythonError();
153
154// helper struct to map the return type of the callPy function
155// default: map to the same type
156template<typename T>
158 using type = T;
159 inline static type convert(const T &r) {
160 return r;
161 }
162};
163// specialization: map PyObject* to PyO
164template<> struct MapRetType<PyObject*> {
165 using type = PyO;
166 inline static PyO convert(PyObject *r) {
167 if(!r)
168 throw std::runtime_error("Internal error: Expected python object but got NULL pointer and no python exception is set.");
169 if(Py_REFCNT(r)<=0)
170 throw std::runtime_error("Internal error: Expected python object but a python object with 0 refcount and no python exception is set.");
171 return PyO(r);
172 }
173};
174
175// helper function to map the argument types of the callPy function
176// default: map to the same type
177template<typename Arg>
178inline Arg convertArg(const Arg &arg) {
179 return arg;
180}
181// specialization:: map PyO to PyObject*
182inline PyObject* convertArg(const PyO &o) {
183 if(o && o.use_count()<=0)
184 throw std::runtime_error("Internal error: access object with reference count <= 0. Check the source code.");
185 return o.get();
186}
187// specialization:: map std::string to const char*
188inline const char* convertArg(const std::string &o) {
189 return o.c_str();
190}
191
193 public:
194 DisableFPE();
195 ~DisableFPE();
196 private:
197 int savedFPE;
198};
199
200// Call Python function func with arguments args.
201// Use the macro CALLPY or CALLPYB, see below.
202template<typename PyRet, typename... PyArgs, typename... CallArgs>
203inline typename MapRetType<PyRet>::type callPy(const char *file, int line, PyRet (*func)(PyArgs...), CallArgs&&... args) {
204 PyRet ret;
205 {
206 // sympy/numpy/others may generate FPEs during calls -> disable FPE exceptions for python calls (save it first to restore it next)
207 DisableFPE disableFPE;
208 ret=func(convertArg(std::forward<CallArgs>(args))...);
209 }
210 if(PyErr_Occurred())
211 throw PythonException(file, line);
212 return MapRetType<PyRet>::convert(ret);
213}
214
215// Macro to call callPy(...)
216// Use this macro to call a python function returning a new reference to a python object or any other return type.
217// Note, if the python function steals a reference of any of this arguments you have to call arg.incRef() on
218// each such arguments.
219#define CALLPY(...) PythonCpp::callPy(__FILE__, __LINE__, __VA_ARGS__)
220
221// Macro to call callPy(...).incRef()
222// Use this macro to call a python function returning a borrowed reference to a python object.
223// Note, if the python function steals a reference of any of this arguments you have to call arg.incRef() on
224// each such arguments.
225#define CALLPYB(...) PythonCpp::callPy(__FILE__, __LINE__, __VA_ARGS__).incRef()
226
227// Initialize python giving main as program name to python.
228// All path in sysPathAppend are added to python's sys.path array.
229// If PYTHONHOME is not set all possiblePrefix dirs are tested for a possible PYTHONHOME
230// and if one is found envvar is set.
231// The calling thread holds the python GIL after this function finished. To release it
232// call PyEval_SaveThread() or PyEval_ReleaseThread(PyThreadState_Get()). In this case ensure to acquire the GIL in each thread before
233// each call to python using the class GilState
234void initializePython(const boost::filesystem::path &main, const std::string &pythonVersion,
235 const std::vector<boost::filesystem::path> &sysPathPrepend={},
236 const std::vector<boost::filesystem::path> &sysPathAppend={},
237 const std::vector<boost::filesystem::path> &possiblePrefix={},
238 const std::vector<boost::filesystem::path> &PATHAppend={});
239
240// a Py_BuildValue variant working like called with CALLPY
241template<typename... Args>
242PyO Py_BuildValue_(const char *format, Args&&... args) {
243 return PyO(Py_BuildValue(format, convertArg(std::forward<Args>(args))...));
244}
245
246}
247
248#endif
Definition: pycppwrapper.h:192
Definition: pycppwrapper.h:85
Definition: pycppwrapper.h:98
Definition: pycppwrapper.h:134
Definition: pycppwrapper.h:157