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