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
42 namespace PythonCpp {
43
44 // make PyRun_String a function
45 inline 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
50 inline 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
55 inline 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
60 inline 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
65 inline 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
70 inline 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
75 inline 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
80 inline int PyUnicode_Check_func(PyObject *p) { return PyUnicode_Check(p); }
81 #undef PyUnicode_Check
82 #define PyUnicode_Check PythonCpp::PyUnicode_Check_func
83
84 // we use this for python object for c++ reference counting
85 class PyO {
86 public:
87 constexpr PyO() = default;
88 explicit PyO(PyObject *src, bool srcIsBorrowedRef=false) : p(src) { if(srcIsBorrowedRef) Py_XINCREF(p); } // use srcIsBorrowedRef=true if src is a borrowed reference
89 PyO(const PyO &r) : p(r.p) { Py_XINCREF(p); }
90 PyO(PyO &&r) noexcept : p(r.p) { r.p=nullptr; }
91 ~PyO() { Py_XDECREF(p); }
92 PyO& operator=(const PyO &r) { Py_XDECREF(p); p=r.p; Py_XINCREF(p); return *this; }
93 PyO& operator=(PyO &&r) noexcept { Py_XDECREF(p); p=r.p; r.p=nullptr; return *this; }
94 void reset() { Py_XDECREF(p); p=nullptr; }
95 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
96 void swap(PyO &r) { PyObject *temp=p; p=r.p; r.p=temp; }
97 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
98 long use_count() const { return p ? Py_REFCNT(p) : 0; }
99 PyObject* operator->() const { return p; }
100 PyO& incRef() { Py_XINCREF(p); return *this; } // use if the caller steals a reference of the returned PyObject
101 operator bool() const { return p!=nullptr; }
102 protected:
103 PyObject *p{nullptr};
104 };
105
106 inline bool operator==(const PyO& l, const PyO& r) { return l.get()==r.get(); }
107 inline bool operator!=(const PyO& l, const PyO& r) { return l.get()!=r.get(); }
108 inline bool operator< (const PyO& l, const PyO& r) { return l.get()< r.get(); }
109 inline bool operator> (const PyO& l, const PyO& r) { return l.get()> r.get(); }
110 inline bool operator<=(const PyO& l, const PyO& r) { return l.get()<=r.get(); }
111 inline bool operator>=(const PyO& l, const PyO& r) { return l.get()>=r.get(); }
112
113 // A Python error exception object.
114 // Stores the file and line number of the C++ file where the error occured.
115 // Stores also the Python error objects type, value and traceback.
116 class PythonException : public std::exception {
117 public:
118 PythonException(const char *file_, int line_);
119 virtual ~PythonException() noexcept override = default;
120 std::string getFile() { return file; }
121 int getLine() { return line; }
122 PyO getType() { return type; }
123 PyO getValue() { return value; }
124 PyO getTraceback() { return traceback; }
125 const char* what() const noexcept override;
126 private:
127 std::string file;
128 int line;
129 PyO type, value, traceback;
130 mutable std::string msg;
131 };
132
133 // check for a python exception and throw a PythonException if one exists
134 void checkPythonError();
135
136 // helper struct to map the return type of the callPy function
137 // default: map to the same type
138 template<typename T>
139 struct MapRetType {
140 using type = T;
141 inline static type convert(const T &r) {
142 return r;
143 }
144 };
145 // specialization: map PyObject* to PyO
146 template<> struct MapRetType<PyObject*> {
147 using type = PyO;
148 inline static PyO convert(PyObject *r) {
149 if(!r)
150 throw std::runtime_error("Internal error: Expected python object but got NULL pointer and no python exception is set.");
151 if(Py_REFCNT(r)<=0)
152 throw std::runtime_error("Internal error: Expected python object but a python object with 0 refcount and no python exception is set.");
153 return PyO(r);
154 }
155 };
156
157 // helper function to map the argument types of the callPy function
158 // default: map to the same type
159 template<typename Arg>
160 inline Arg convertArg(const Arg &arg) {
161 return arg;
162 }
163 // specialization:: map PyO to PyObject*
164 inline PyObject* convertArg(const PyO &o) {
165 if(o && o.use_count()<=0)
166 throw std::runtime_error("Internal error: access object with reference count <= 0. Check the source code.");
167 return o.get();
168 }
169 // specialization:: map std::string to const char*
170 inline const char* convertArg(const std::string &o) {
171 return o.c_str();
172 }
173
174 class DisableFPE {
175 public:
176 DisableFPE();
177 ~DisableFPE();
178 private:
179 int savedFPE;
180 };
181
182 // Call Python function func with arguments args.
183 // Use the macro CALLPY or CALLPYB, see below.
184 template<typename PyRet, typename... PyArgs, typename... CallArgs>
185 inline typename MapRetType<PyRet>::type callPy(const char *file, int line, PyRet (*func)(PyArgs...), CallArgs&&... args) {
186 PyRet ret;
187 {
188 // sympy/numpy/others may generate FPEs during calls -> disable FPE exceptions for python calls (save it first to restore it next)
189 DisableFPE disableFPE;
190 ret=func(convertArg(std::forward<CallArgs>(args))...);
191 }
192 if(PyErr_Occurred())
193 throw PythonException(file, line);
194 return MapRetType<PyRet>::convert(ret);
195 }
196
197 // Macro to call callPy(...)
198 // Use this macro to call a python function returning a new reference to a python object or any other return type.
199 // Note, if the python function steals a reference of any of this arguments you have to call arg.incRef() on
200 // each such arguments.
201 #define CALLPY(...) PythonCpp::callPy(__FILE__, __LINE__, __VA_ARGS__)
202
203 // Macro to call callPy(...).incRef()
204 // Use this macro to call a python function returning a borrowed reference to a python object.
205 // Note, if the python function steals a reference of any of this arguments you have to call arg.incRef() on
206 // each such arguments.
207 #define CALLPYB(...) PythonCpp::callPy(__FILE__, __LINE__, __VA_ARGS__).incRef()
208
209 // Initialize python giving main as program name to python.
210 // All path in sysPathAppend are added to python's sys.path array.
211 // If PYTHONHOME is not set all possiblePrefix dirs are tested for a possible PYTHONHOME
212 // and if one is found envvar is set
213 void initializePython(const boost::filesystem::path &main, const std::string &pythonVersion,
214 const std::vector<boost::filesystem::path> &sysPathPrepend={},
215 const std::vector<boost::filesystem::path> &sysPathAppend={},
216 const std::vector<boost::filesystem::path> &possiblePrefix={},
217 const std::vector<boost::filesystem::path> &PATHAppend={});
218
219 // a Py_BuildValue variant working like called with CALLPY
220 template<typename... Args>
221 PyO Py_BuildValue_(const char *format, Args&&... args) {
222 return PyO(Py_BuildValue(format, convertArg(std::forward<Args>(args))...));
223 }
224
225 }
226
227 #endif
PythonCpp::PyO
Definition: pycppwrapper.h:85
PythonCpp::PythonException
Definition: pycppwrapper.h:116
PythonCpp::MapRetType
Definition: pycppwrapper.h:139
PythonCpp::DisableFPE
Definition: pycppwrapper.h:174