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