Uncrustify all
[alexxy/gromacs-pyapi.git] / src / sip / options / pyoptionsholder.sip
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2014,2015, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 %ModuleHeaderCode
37 #include <exception>
38 #include "gromacs/options/basicoptions.h"
39 #include "gromacs/options/filenameoption.h"
40 #include "gromacs/selection/selectionoption.h"
41 #include <map>
42 #include <string>
43
44 class PyOptionsHolder {
45     public:
46         class DuplicateOption: public std::exception {
47             public:
48                 virtual const char* what() const noexcept;
49         };
50         class StringList {
51             public:
52                 StringList(const std::vector < std::string > *);
53                 StringList(const std::string*, size_t);
54                 const char       * operator[] (size_t);
55             private:
56                 const std::string *list;
57                 size_t             size;
58                 const              std::vector < std::string > *vector;
59         };
60         gmx::DoubleOption doubleOption(const char*, size_t = 1, double* = NULL);
61         gmx::IntegerOption integerOption(const char*, size_t = 1, int* = NULL);
62         gmx::StringOption stringOption(const char*, size_t = 1, const char* = NULL);
63         gmx::BooleanOption booleanOption(const char*, size_t = 1, bool = 0);
64         gmx::SelectionOption selectionOption(const char*, size_t = 1);
65         gmx::FileNameOption fileNameOption(const char*, size_t = 1);
66         PyObject* get_value(const char*);
67         ~PyOptionsHolder();
68     private:
69         struct option_value {
70             void       *value;
71             const char *type;
72             int        *count;
73             bool        vector;
74             const char *name;
75         };
76         std::map < std::string, option_value > storage;
77         template < typename T, typename U > U createStorage(const char*, const char*, int, const T* = 0);
78         template < typename T > PyObject* buildValue(const char*, const option_value &, const sipTypeDef* = NULL, size_t = 0);
79         template < typename T > void deleteStorage(const option_value &);
80 };
81 %End
82
83 %ModuleCode
84 #include "gromacs/utility/gmxassert.h"
85
86 const char* PyOptionsHolder::DuplicateOption::what() const noexcept {
87     return "This option is already defined";
88 }
89
90 PyOptionsHolder::StringList::StringList(const std::vector < std::string > *vector) :
91     list(NULL), size(vector->size()), vector(vector)
92 {}
93
94 PyOptionsHolder::StringList::StringList(const std::string *list, size_t size) :
95     list(list), size(size), vector(NULL)
96 {}
97
98 const char* PyOptionsHolder::StringList::operator[] (size_t i) {
99     if (i >= size)
100     {
101         return NULL;
102     }
103     return vector ? (*vector)[i].data() : list[i].data();
104 }
105
106 template < typename T, typename U > U PyOptionsHolder::createStorage(const char *name, const char *type, int count, const T *def)
107 {
108     if (storage.count(name))
109     {
110         throw DuplicateOption();
111     }
112
113     U option(name);
114
115     option_value value;
116     value.type  = type;
117     value.count = new int;
118     value.name  = name;
119
120     if (count == 0)   // std::vector of values
121     {
122         auto *store = new std::vector < T > ();
123         option.storeVector(store);
124         option.multiValue();
125         option.storeCount(value.count);
126
127         *value.count = 0;
128         if (def)
129         {
130             store->push_back(*def);
131             *value.count = 1;
132         }
133
134         value.value  = store;
135         value.vector = true;
136     }
137     else     // exactly `count` values
138     {
139         auto *store = new T[count];
140         if (def)
141         {
142             store[0] = *def;
143         }
144
145         option.store(store);
146         if (count > 1)
147         {
148             option.valueCount(count);
149         }
150
151         value.value  = store;
152         value.vector = false;
153         *value.count = count;
154     }
155
156     storage[name] = value;
157     return option;
158 }
159
160 gmx::DoubleOption PyOptionsHolder::doubleOption(const char *name, size_t count, double *def)
161 {
162     gmx::DoubleOption option = createStorage < double, gmx::DoubleOption > (name, "d", count, def);
163
164     return option;
165 }
166
167 gmx::IntegerOption PyOptionsHolder::integerOption(const char *name, size_t count, int *def)
168 {
169     gmx::IntegerOption option = createStorage < int, gmx::IntegerOption > (name, "i", count, def);
170
171     return option;
172 }
173
174 gmx::StringOption PyOptionsHolder::stringOption(const char *name, size_t count, const char* def)
175 {
176     std::string *s_def = NULL;
177     if (def)
178     {
179         s_def = new std::string(def);
180     }
181
182     gmx::StringOption option = createStorage < std::string, gmx::StringOption > (name, "A", count, s_def);
183     delete            s_def;
184
185     return option;
186 }
187
188 // FIXME: Unify with the rest
189 gmx::BooleanOption PyOptionsHolder::booleanOption(const char *name, size_t count, bool def)
190 {
191     if (storage.count(name))
192     {
193         throw DuplicateOption();
194     }
195
196     bool *store = new bool;
197     *store = def;
198     gmx::BooleanOption option(name);
199     option.store(store);
200     storage[name] = {store, "b"};
201
202     return option;
203 }
204
205 gmx::SelectionOption PyOptionsHolder::selectionOption(const char *name, size_t count)
206 {
207     gmx::SelectionOption option = createStorage < gmx::Selection, gmx::SelectionOption > (name, "S", count);
208
209     return option;
210 }
211
212 gmx::FileNameOption PyOptionsHolder::fileNameOption(const char *name, size_t count)
213 {
214     gmx::FileNameOption option = createStorage < std::string, gmx::FileNameOption > (name, "f", count);
215
216     return option;
217 }
218
219 template < typename T > PyObject* PyOptionsHolder::buildValue(const char *sipType, const PyOptionsHolder::option_value &value, const sipTypeDef *td, size_t size)
220 {
221     int count = *value.count;
222     T  *store = !value.vector ? (T*) value.value : ((std::vector < T > *)value.value)->data();
223
224     if (count == 1 && !value.vector)
225     {
226         if (td)
227         {
228             return sipConvertFromType(store, td, NULL);
229         }
230         else
231         {
232             return sipBuildResult(NULL, sipType, *store);
233         }
234     }
235     else
236     {
237         if (td)
238         {
239             return sipConvertToTypedArray(store, td, NULL, size, count, SIP_READ_ONLY);
240         }
241         else
242         {
243             return sipConvertToArray(store, sipType, count, SIP_READ_ONLY);
244         }
245     }
246 }
247
248
249 PyObject* PyOptionsHolder::get_value(const char *name)
250 {
251     if (!storage.count(name))
252     {
253         return NULL;
254     }
255
256     option_value v = storage[name];
257     switch (*v.type)
258     {
259         case 'd': // double
260             return buildValue < double > ("d", v);
261             break;
262         case 'i': // int
263             return buildValue < int > ("i", v);
264             break;
265         case 'A': // std::string
266         case 'f': // FileNameOption
267             if (v.vector)
268             {
269                 return sipConvertFromType(new PyOptionsHolder::StringList(((std::vector < std::string > *)v.value)), sipType_PyOptionsHolder_StringList, Py_None);
270             }
271             else if (*v.count == 1)
272             {
273                 return sipBuildResult(NULL, "A", ((std::string*) v.value)->data());
274             }
275             else
276             {
277                 return sipConvertFromType(new PyOptionsHolder::StringList((std::string*) v.value, *v.count), sipType_PyOptionsHolder_StringList, Py_None);
278             }
279             break;
280         case 'b': // bool
281             // TODO: support vector bool options
282             return sipBuildResult(NULL, v.type, *((bool*) v.value));
283             break;
284         case 'S': // Selection
285             return buildValue < gmx::Selection > (NULL, v, sipType_Selection, sizeof(gmx::Selection));
286             break;
287     }
288
289     GMX_ASSERT(false, "Some type is not handled in PyOptionsHolder.get_value");
290     return NULL;
291 }
292
293 template < typename T > void PyOptionsHolder::deleteStorage(const PyOptionsHolder::option_value &value)
294 {
295     if (value.vector)
296     {
297         delete (std::vector < T > *) value.value;
298     }
299     else
300     {
301         delete[] (T*) value.value;
302     }
303
304     delete value.count;
305 }
306
307 PyOptionsHolder::~PyOptionsHolder()
308 {
309     for (auto e : storage)
310     {
311         switch (*e.second.type)
312         {
313             case 'd': // double
314                 deleteStorage < double > (e.second);
315                 break;
316             case 'i': // int
317                 deleteStorage < int > (e.second);
318                 break;
319             case 'A': // std::string
320             case 'f': // FileNameOption (the storage type is still std::string)
321                 deleteStorage < std::string > (e.second);
322                 break;
323             case 'b': // bool
324                 delete (bool*) e.second.value;
325                 break;
326             case 'S': // Selection
327                 deleteStorage < gmx::Selection > (e.second);
328                 break;
329         }
330     }
331 }
332 %End
333
334 %Exception PyOptionsHolder::DuplicateOption {
335     %RaiseCode
336     SIP_BLOCK_THREADS;
337     PyErr_SetString(PyExc_ValueError, sipExceptionRef.what());
338     SIP_UNBLOCK_THREADS;
339     %End
340 };
341
342 class PyOptionsHolder {
343     %TypeHeaderCode
344 #include "gromacs/options/basicoptions.h"
345     %End
346
347     public:
348         class StringList /NoDefaultCtors/ {
349             public:
350                 const char* operator[] (int);
351                 %MethodCode
352                 sipRes = (*sipCpp)[a0];
353
354                 if (!sipRes)
355                 {
356                     SIP_BLOCK_THREADS;
357                     PyErr_SetString(PyExc_IndexError, "Index out of range");
358                     SIP_UNBLOCK_THREADS;
359                     sipIsErr = 1;
360                 }
361                 %End
362         };
363         // These methods are given twice to workaround sip setting 0 for when '*def = NULL' option is not given
364         DoubleOption doubleOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
365         DoubleOption doubleOption(const char *name /KeepReference/, int count, const double *def) throw (PyOptionsHolder::DuplicateOption);
366
367         IntegerOption integerOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
368         IntegerOption integerOption(const char *name /KeepReference/, int count, const int *def) throw (PyOptionsHolder::DuplicateOption);
369
370         StringOption stringOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
371         StringOption stringOption(const char *name /KeepReference/, int count, const char *def) throw (PyOptionsHolder::DuplicateOption);
372
373         BooleanOption booleanOption(const char *name /KeepReference/, int count = 1, bool def = 0) throw (PyOptionsHolder::DuplicateOption);
374
375         SelectionOption selectionOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
376
377         FileNameOption fileNameOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
378         SIP_PYOBJECT __getitem__(const char *name);
379         %MethodCode
380         sipRes = sipCpp->get_value(a0);
381
382         if (!sipRes)
383         {
384             SIP_BLOCK_THREADS;
385             PyErr_SetString(PyExc_KeyError, "Invalid option name");
386             SIP_UNBLOCK_THREADS;
387             sipIsErr = 1;
388         }
389         %End
390 };