Salome HOME
Add COPYING license file
[tools/py2cpp.git] / src / TypeConversions.hxx
1 // Copyright (C) 2019-2024 EDF
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 #ifndef PY2CPP_TYPECONVERSIONS_HXX
20 #define PY2CPP_TYPECONVERSIONS_HXX
21 #include "py2cppExports.hxx"
22 #include <Python.h>
23
24 #include <list>
25 #include <vector>
26 #include <map>
27 #include <string>
28 #include <tuple>
29 #include "PyPtr.hxx"
30 #include "Errors.hxx"
31
32 namespace py2cpp
33 {
34 /*!
35  * toPy functions return a new python object built from a c++ object.
36  * The conversion is always possible and it does not throw exceptions.
37  */
38 PY2CPP_EXPORT PyObject * toPy(int);
39 PY2CPP_EXPORT PyObject * toPy(unsigned int);
40 PY2CPP_EXPORT PyObject * toPy(bool);
41 PY2CPP_EXPORT PyObject * toPy(double);
42 PY2CPP_EXPORT PyObject * toPy(const std::string&);
43 PY2CPP_EXPORT PyObject * toPy(const char*);
44 PY2CPP_EXPORT PyObject * toPy(PyObject *);
45 PY2CPP_EXPORT PyObject * toPy(const PyPtr&);
46 template <class T>
47 PY2CPP_EXPORT PyObject * toPy(const std::vector<T>& values);
48 template <class T>
49 PY2CPP_EXPORT PyObject * toPy(const std::list<T>& values);
50 template <class K, class V>
51 PY2CPP_EXPORT PyObject * toPy(const std::map<K, V>& values);
52 template<class ...Ts>
53 PY2CPP_EXPORT PyObject * toPy(const std::tuple<Ts...>& vars );
54
55 /*!
56  * fromPy functions convert a python object to a c++ object.
57  * If the convertion fails, the ConversionCheck object returned contains the
58  * error message. No exception is thrown.
59  */
60 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *, int&);
61 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *, unsigned int&);
62 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *, bool&);
63 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *, double&);
64 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *, std::string&);
65 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *, PyObject *&);
66 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *, PyPtr&);
67 template<class ...Ts>
68 PY2CPP_EXPORT ConversionCheck fromPy(PyObject * obj, std::tuple<Ts&...>& vars);
69 template <class T>
70 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *obj, std::vector<T>& result);
71 template <class T>
72 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *obj, std::list<T>& result);
73 template <class K, class V>
74 PY2CPP_EXPORT ConversionCheck fromPy( PyObject *obj, std::map<K, V>& result);
75
76 /*! This templated version  of fromPy throws ConversionException if the
77  * conversion fails.
78  */
79 template<class T>
80 PY2CPP_EXPORT T fromPy( PyObject *po);
81
82 // These versions of fromPy and toPy use PyPtr instead of PyObject *
83 template<class T>
84 PY2CPP_EXPORT T fromPyPtr( const PyPtr& py);
85 template<class T>
86 PY2CPP_EXPORT ConversionCheck fromPyPtr( const PyPtr& py, T& var);
87 template<class T>
88 PY2CPP_EXPORT PyPtr toPyPtr(const T& v);
89 template<>
90 PY2CPP_EXPORT PyPtr fromPy<PyPtr>( PyObject *po);
91
92 ////////////////////////////////////////////////////////////////////////////////
93 // Template implementations
94 ////////////////////////////////////////////////////////////////////////////////
95
96 // std::tuple
97 template<std::size_t I = 0, class ...Ts>
98 inline typename std::enable_if<I == sizeof...(Ts), void>::type
99 addInPyTuple(PyObject * result, const std::tuple<Ts...>& vars )
100 {
101 }
102
103 template<std::size_t I = 0, class ...Ts>
104 inline typename std::enable_if<I < sizeof...(Ts), void>::type
105 addInPyTuple(PyObject * result, const std::tuple<Ts...>& vars )
106 {
107   PyObject * obj = toPy(std::get<I>(vars));
108   PyTuple_SetItem(result, I, obj);
109   addInPyTuple<I+1, Ts... >(result, vars);
110 }
111
112 template<class ...Ts>
113 PyObject * toPy(const std::tuple<Ts...>& vars )
114 {
115   PyObject * result = PyTuple_New(sizeof...(Ts));
116   addInPyTuple<0, Ts... >(result, vars);
117   return result;
118 }
119
120 // std containers
121 template <class T>
122 PyObject * toPy(const std::vector<T>& values)
123 {
124   PyObject * result = PyList_New(values.size());
125   for(std::size_t i = 0; i < values.size(); ++i)
126     PyList_SetItem(result, i, toPy(values[i]));
127   return result;
128 }
129
130 template <class T>
131 PyObject * toPy(const std::list<T>& values)
132 {
133   PyObject * result = PyList_New(values.size());
134   std::size_t i = 0;
135   for(const T& it : values)
136   {
137     PyList_SetItem(result, i, toPy(it));
138     ++i;
139   }
140   return result;
141 }
142
143 template <class K, class V>
144 PyObject * toPy(const std::map<K, V>& values)
145 {
146   PyObject * result = PyDict_New();
147   for(const auto& it: values)
148   {
149     PyPtr pyKey(toPy(it.first));
150     PyPtr pyValue(toPy(it.second));
151     PyDict_SetItem(result, pyKey.get(), pyValue.get());
152   }
153   return result;
154 }
155
156 template<std::size_t I = 0, class ...Ts>
157 inline typename std::enable_if<I == sizeof...(Ts), ConversionCheck>::type
158 getFromPyTuple(PyObject * tup, std::tuple<Ts&...>& vars )
159 {
160   return ConversionCheck();
161 }
162
163 template<std::size_t I = 0, class ...Ts>
164 inline typename std::enable_if<I < sizeof...(Ts), ConversionCheck>::type
165 getFromPyTuple(PyObject * tup, std::tuple<Ts&...>& vars )
166 {
167   ConversionCheck check;
168   PyObject * obj = PyTuple_GetItem(tup, I);
169   typedef typename std::tuple_element<I, std::tuple<Ts...> >::type T;
170   check.addError(fromPy(obj, std::get<I>(vars)));
171   if(check)
172     check.addError(getFromPyTuple<I+1, Ts...>(tup, vars));
173   return check;
174 }
175
176 template<class ...Ts>
177 ConversionCheck fromPy(PyObject * obj, std::tuple<Ts&...>& vars)
178 {
179   ConversionCheck check;
180   if(obj)
181   {
182     if(PyTuple_Check(obj) && 
183       PyTuple_Size(obj) == std::tuple_size<std::tuple<Ts&...> >::value)
184     {
185       check.addError(getFromPyTuple<0, Ts...>(obj, vars));
186     }
187     else
188       if(1 == std::tuple_size<std::tuple<Ts&...> >::value)
189       {
190         check.addError(fromPy(obj, std::get<0>(vars)));
191       }
192     if(!check)
193       check.addError("std::tuple", obj);
194   }
195   else
196     check.addError("std::tuple", obj);
197   return check;
198 }
199
200 template <class T>
201 ConversionCheck fromPy( PyObject *obj, std::vector<T>& result)
202 {
203   ConversionCheck check;
204   if(PyList_Check(obj))
205   {
206     result.clear();
207     std::size_t size = PyList_Size(obj);
208     result.resize(size);
209     for(std::size_t i=0; i < size && check; i++)
210       check.addError(fromPy(PyList_GetItem(obj, i), result[i]));
211     if(!check)
212       check.addError("std::vector", obj);
213   }
214   else if(PyTuple_Check(obj))
215   {
216     result.clear();
217     std::size_t size = PyTuple_Size(obj);
218     result.resize(size);
219     for(std::size_t i=0; i < size && check; i++)
220       check.addError(fromPy(PyTuple_GetItem(obj, i), result[i]));
221     if(!check)
222       check.addError("std::vector", obj);
223   }
224   else
225     check.addError("std::vector", obj);
226   return check;
227 }
228
229 template <class T>
230 ConversionCheck fromPy( PyObject *obj, std::list<T>& result)
231 {
232   ConversionCheck check;
233   if(PyList_Check(obj))
234   {
235     result.clear();
236     std::size_t size = PyList_Size(obj);
237     result.resize(size); //result will have "size" default constructed elements.
238     std::size_t i = 0;
239     for(T& it : result)
240     {
241       check.addError(fromPy(PyList_GetItem(obj, i), it));
242       if(!check)
243       {
244         check.addError("std::list", obj);
245         break;
246       }
247       ++i;
248     }
249   }
250   else if(PyTuple_Check(obj))
251   {
252     result.clear();
253     std::size_t size = PyTuple_Size(obj);
254     result.resize(size); //result will have "size" default constructed elements.
255     std::size_t i = 0;
256     for(T& it : result)
257     {
258       check.addError(fromPy(PyTuple_GetItem(obj, i), it));
259       if(!check)
260       {
261         check.addError("std::list", obj);
262         break;
263       }
264       ++i;
265     }
266   }
267   else
268     check.addError("std::list", obj);
269   return check;
270 }
271
272 template <class K, class V>
273 ConversionCheck fromPy( PyObject *obj, std::map<K, V>& result)
274 {
275   ConversionCheck check;
276   if(PyDict_Check(obj))
277   {
278     result.clear();
279     PyPtr pyKeys(PyDict_Keys(obj));
280     std::size_t size = PyList_Size(pyKeys.get());
281     for(std::size_t i =0; i < size && check; ++i)
282     {
283       PyObject * curKey = PyList_GetItem(pyKeys.get(), i);
284       K key;
285       V value;
286       check.addError(fromPy(curKey, key));
287       if(check)
288         check.addError(fromPy(PyDict_GetItem(obj, curKey), value));
289       if(check)
290         result[key]=value;
291       else
292         check.addError("std::map", obj);
293     }
294   }
295   else
296     check.addError("std::map", obj);
297   return check;
298 }
299
300 // PyPtr
301 template<class T>
302 T fromPyPtr( const PyPtr& py)
303 {
304   T result;
305   fromPy(py.get(), result);
306   return result;
307 }
308
309 template<class T>
310 ConversionCheck fromPyPtr( const PyPtr& py, T& var)
311 {
312   return fromPy(py.get(), var);
313 }
314
315 template<class T>
316 PyPtr toPyPtr(const T& v)
317 {
318   return PyPtr(toPy(v));
319 }
320
321 template<class T>
322 T fromPy( PyObject *po)
323 {
324   T result;
325   ConversionCheck check;
326   check = fromPy(po, result);
327   if(!check)
328     throw ConversionException(check.getMessage());
329   return result;
330 }
331
332 }
333 #endif //PY2CPP_TYPECONVERSIONS_HXX