0f775761d29dd71cbbd365cf8e3a1b18f1f4c869
[alexxy/gromacs.git] / src / python / sip / options / pyoptionsholder.sip
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2014, 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 throw();
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 throw() {
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         return NULL;
101     return vector ? (*vector)[i].data() : list[i].data();
102 }
103
104 template<typename T, typename U> U PyOptionsHolder::createStorage(const char *name, const char *type, int count, const T *def) {
105     if (storage.count(name))
106         throw DuplicateOption();
107
108     U option(name);
109
110     option_value value;
111     value.type = type;
112     value.count = new int;
113     value.name = name;
114
115     if (count == 0) { // std::vector of values
116         std::vector<T> *store = new std::vector<T>();
117         option.storeVector(store);
118         option.multiValue();
119         option.storeCount(value.count);
120
121         *value.count = 0;
122         if (def) {
123             store->push_back(*def);
124             *value.count = 1;
125         }
126
127         value.value = store;
128         value.vector = true;
129     } else { // exactly `count` values
130         T *store = new T[count];
131         if (def)
132             store[0] = *def;
133
134         option.store(store);
135         if (count > 1)
136             option.valueCount(count);
137
138         value.value = store;
139         value.vector = false;
140         *value.count = count;
141     }
142
143     storage[name] = value;
144     return option;
145 }
146
147 gmx::DoubleOption PyOptionsHolder::doubleOption(const char *name, size_t count, double *def) {
148     gmx::DoubleOption option = createStorage<double, gmx::DoubleOption>(name, "d", count, def);
149
150     return option;
151 }
152
153 gmx::IntegerOption PyOptionsHolder::integerOption(const char *name, size_t count, int *def) {
154     gmx::IntegerOption option = createStorage<int, gmx::IntegerOption>(name, "i", count, def);
155
156     return option;
157 }
158
159 gmx::StringOption PyOptionsHolder::stringOption(const char *name, size_t count, const char* def) {
160     std::string *s_def = NULL;
161     if (def)
162         s_def = new std::string(def);
163
164     gmx::StringOption option = createStorage<std::string, gmx::StringOption>(name, "A", count, s_def);
165     delete s_def;
166
167     return option;
168 }
169
170 // FIXME: Unify with the rest
171 gmx::BooleanOption PyOptionsHolder::booleanOption(const char *name, size_t count, bool def) {
172     if (storage.count(name))
173         throw DuplicateOption();
174
175     bool *store = new bool;
176     *store = def;
177     gmx::BooleanOption option(name);
178     option.store(store);
179     option_value value = {store, "b"};
180     storage[name] = value;
181
182     return option;
183 }
184
185 gmx::SelectionOption PyOptionsHolder::selectionOption(const char *name, size_t count) {
186     gmx::SelectionOption option = createStorage<gmx::Selection, gmx::SelectionOption>(name, "S", count);
187
188     return option;
189 }
190
191 gmx::FileNameOption PyOptionsHolder::fileNameOption(const char *name, size_t count) {
192     gmx::FileNameOption option = createStorage<std::string, gmx::FileNameOption>(name, "f", count);
193
194     return option;
195 }
196
197 template<typename T> PyObject* PyOptionsHolder::buildValue(const char *sipType, const PyOptionsHolder::option_value &value, const sipTypeDef *td, size_t size) {
198     int count = *value.count;
199     T *store = !value.vector ? (T*) value.value : ((std::vector<T>*) value.value)->data();
200
201     if (count == 1 && !value.vector) {
202         if (td)
203             return sipConvertFromType(store, td, NULL);
204         else
205             return sipBuildResult(NULL, sipType, *store);
206     } else {
207         if (td)
208             return sipConvertToTypedArray(store, td, NULL, size, count, SIP_READ_ONLY);
209         else
210             return sipConvertToArray(store, sipType, count, SIP_READ_ONLY);
211     }
212 }
213
214
215 PyObject* PyOptionsHolder::get_value(const char *name) {
216     if (!storage.count(name))
217         return NULL;
218
219     option_value v = storage[name];
220     switch (*v.type) {
221     case 'd': // double
222         return buildValue<double>("d", v);
223         break;
224     case 'i': // int
225         return buildValue<int>("i", v);
226         break;
227     case 'A': // std::string
228     case 'f': // FileNameOption
229         if (v.vector)
230             return sipConvertFromType(new PyOptionsHolder::StringList(((std::vector<std::string>*) v.value)), sipType_PyOptionsHolder_StringList, Py_None);
231         else if (*v.count == 1)
232             return sipBuildResult(NULL, "A", ((std::string*) v.value)->data());
233         else
234             return sipConvertFromType(new PyOptionsHolder::StringList((std::string*) v.value, *v.count), sipType_PyOptionsHolder_StringList, Py_None);
235         break;
236     case 'b': // bool
237         // TODO: support vector bool options
238         return sipBuildResult(NULL, v.type, *((bool*) v.value));
239         break;
240     case 'S': // Selection
241         return buildValue<gmx::Selection>(NULL, v, sipType_Selection, sizeof(gmx::Selection));
242         break;
243     }
244
245     GMX_ASSERT(false, "Some type is not handled in PyOptionsHolder.get_value");
246     return NULL;
247 }
248
249 template<typename T> void PyOptionsHolder::deleteStorage(const PyOptionsHolder::option_value &value) {
250     if (value.vector)
251         delete (std::vector<T> *) value.value;
252     else
253         delete[] (T*) value.value;
254
255     delete value.count;
256 }
257
258 PyOptionsHolder::~PyOptionsHolder() {
259     for (std::map<std::string, option_value>::iterator it = storage.begin(); it != storage.end(); it++)
260         switch (*(it->second.type)) {
261             case 'd': // double
262                 deleteStorage<double>(it->second);
263                 break;
264             case 'i': // int
265                 deleteStorage<int>(it->second);
266                 break;
267             case 'A': // std::string
268             case 'f': // FileNameOption (the storage type is still std::string)
269                 deleteStorage<std::string>(it->second);
270                 break;
271             case 'b': // bool
272                 delete (bool*) it->second.value;
273                 break;
274             case 'S': // Selection
275                 deleteStorage<gmx::Selection>(it->second);
276                 break;
277         }
278 }
279 %End
280
281 %Exception PyOptionsHolder::DuplicateOption {
282 %RaiseCode
283         SIP_BLOCK_THREADS;
284         PyErr_SetString(PyExc_ValueError, sipExceptionRef.what());
285         SIP_UNBLOCK_THREADS;
286 %End
287 };
288
289 class PyOptionsHolder {
290 %TypeHeaderCode
291 #include "gromacs/options/basicoptions.h"
292 %End
293
294 public:
295     class StringList /NoDefaultCtors/ {
296     public:
297         const char* operator[] (int);
298         %MethodCode
299             sipRes = (*sipCpp)[a0];
300
301             if (!sipRes) {
302                 SIP_BLOCK_THREADS;
303                 PyErr_SetString(PyExc_IndexError, "Index out of range");
304                 SIP_UNBLOCK_THREADS;
305                 sipIsErr = 1;
306             }
307         %End
308     };
309     // These methods are given twice to workaround sip setting 0 for when '*def = NULL' option is not given
310     DoubleOption doubleOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
311     DoubleOption doubleOption(const char *name /KeepReference/, int count, const double *def) throw (PyOptionsHolder::DuplicateOption);
312
313     IntegerOption integerOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
314     IntegerOption integerOption(const char *name /KeepReference/, int count, const int *def) throw (PyOptionsHolder::DuplicateOption);
315
316     StringOption stringOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
317     StringOption stringOption(const char *name /KeepReference/, int count, const char *def) throw (PyOptionsHolder::DuplicateOption);
318
319     BooleanOption booleanOption(const char *name /KeepReference/, int count = 1, bool def = 0) throw (PyOptionsHolder::DuplicateOption);
320
321     SelectionOption selectionOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
322
323     FileNameOption fileNameOption(const char *name /KeepReference/, int count = 1) throw (PyOptionsHolder::DuplicateOption);
324     SIP_PYOBJECT __getitem__(const char *name);
325     %MethodCode
326         sipRes = sipCpp->get_value(a0);
327
328         if (!sipRes) {
329             SIP_BLOCK_THREADS;
330             PyErr_SetString(PyExc_KeyError, "Invalid option name");
331             SIP_UNBLOCK_THREADS;
332             sipIsErr = 1;
333         }
334     %End
335 };