comparison PyTypeInterface.h @ 31:4f1894c7591b vampy2

Created Vampy2 branch
author fazekasgy
date Sun, 20 Sep 2009 17:31:20 +0000
parents
children a8231788216c
comparison
equal deleted inserted replaced
28:5139bf30f208 31:4f1894c7591b
1 /*
2
3 Type safe conversion utilities from Python types to C/C++ types,
4 mainly using Py/C API macros.
5
6 */
7
8 #ifndef _PY_TYPE_INTERFACE_H_
9 #define _PY_TYPE_INTERFACE_H_
10
11 #include "vamp-sdk/Plugin.h"
12 #include <Python.h>
13 #include "PyExtensionModule.h"
14 #include <vector>
15 #include <queue>
16 #include <string>
17 //#include <typeinfo>
18
19
20 using std::cerr;
21 using std::endl;
22
23 namespace o {
24 enum eOutDescriptors {
25 not_found,
26 identifier,
27 name,
28 description,
29 unit,
30 hasFixedBinCount,
31 binCount,
32 binNames,
33 hasKnownExtents,
34 minValue,
35 maxValue,
36 isQuantized,
37 quantizeStep,
38 sampleType,
39 sampleRate,
40 hasDuration,
41 endNode
42 };
43 }
44
45 namespace p {
46 enum eParmDescriptors {
47 not_found,
48 identifier,
49 name,
50 description,
51 unit,
52 minValue,
53 maxValue,
54 defaultValue,
55 isQuantized,
56 quantizeStep
57 };
58 }
59
60 enum eSampleTypes {
61 OneSamplePerStep,
62 FixedSampleRate,
63 VariableSampleRate
64 };
65
66 enum eFeatureFields {
67 unknown,
68 hasTimestamp,
69 timeStamp,
70 hasDuration,
71 duration,
72 values,
73 label
74 };
75
76
77 /// sutructure of NumPy array interface:
78 /// this is all we need to support numpy without direct dependency
79 typedef struct {
80 int two; /* contains the integer 2 -- simple sanity check */
81 int nd; /* number of dimensions */
82 char typekind; /* kind in array --- character code of typestr */
83 int itemsize; /* size of each element */
84 int flags; /* flags indicating how the data should be interpreted */
85 /* must set ARR_HAS_DESCR bit to validate descr */
86 Py_intptr_t *shape; /* A length-nd array of shape information */
87 Py_intptr_t *strides; /* A length-nd array of stride information */
88 void *data; /* A pointer to the first element of the array */
89 PyObject *descr; /* NULL or data-description (same as descr key */
90 /* of __array_interface__) -- must set ARR_HAS_DESCR */
91 /* flag or this will be ignored. */
92 } PyArrayInterface;
93
94 /* C++ mapping of PyNone Type*/
95 typedef struct NoneType {};
96
97 class PyTypeInterface
98 {
99 public:
100 PyTypeInterface();
101 ~PyTypeInterface();
102
103 // Data
104 class ValueError
105 {
106 public:
107 ValueError() {}
108 ValueError(std::string m, bool s) : message(m),strict(s) {}
109 std::string location;
110 std::string message;
111 bool strict;
112 std::string get() const { return message + "\nLocation: " + location + "\n";}
113 void print() const { cerr << get(); }
114 };
115
116 // Utilities
117 void setStrictTypingFlag(bool b) {m_strict = b;}
118 const ValueError &lastError() const;
119 ValueError getError() const;
120 std::string PyValue_Get_TypeName(PyObject*) const;
121 bool initMaps() const;
122
123 // Basic type conversion: Python to C++
124 float PyValue_To_Float(PyObject*) const;
125 size_t PyValue_To_Size_t(PyObject*) const;
126 bool PyValue_To_Bool(PyObject*) const;
127 std::string PyValue_To_String(PyObject*) const;
128 // int PyValue_To_Int(PyObject*) const;
129
130 // C++ to Python
131 PyObject *PyValue_From_CValue(const char*) const;
132 PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); }
133 PyObject *PyValue_From_CValue(size_t) const;
134 PyObject *PyValue_From_CValue(double) const;
135 PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); }
136 PyObject *PyValue_From_CValue(bool) const;
137
138 // Sequence types
139 std::vector<std::string> PyValue_To_StringVector (PyObject*) const;
140 std::vector<float> PyValue_To_FloatVector (PyObject*) const;
141
142 // Numpy types
143 float* getNumPyObjectData(PyObject *object, int &length) const;
144
145
146 /* Template functions */
147
148
149 /// Common wrappers to set a value in one of these structs. (to be used in template functions)
150 void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
151 void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
152 bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
153 PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const
154 {
155 if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue);
156 if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue);
157 if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue);
158 return NULL;
159 }
160
161
162 template<typename RET>
163 RET PyTypeInterface::PyValue_To_VampDescriptor(PyObject* pyValue) const
164 //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature
165 {
166 PyObject* pyDict;
167
168 // Descriptors encoded as dicts
169 pyDict = GetDescriptor_As_Dict(pyValue);
170 if (!pyDict) pyDict = pyValue;
171
172 // TODO: support full mapping protocol as fallback.
173 if (!PyDict_Check(pyDict)) {
174 setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict);
175 return RET();
176 }
177
178 Py_ssize_t pyPos = 0;
179 PyObject *pyKey, *pyDictValue;
180 initMaps();
181 RET rd;
182
183 //Python Dictionary Iterator:
184 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
185 {
186 std::string key = PyValue_To_String(pyKey);
187 SetValue(rd,key,pyDictValue);
188 if (m_error) {
189 _lastError().location += "parameter: '" + key + "'";//"' descriptor: '" + rd.identifier + "'";
190 }
191 }
192 if (!m_errorQueue.empty()) m_error = true;
193 return rd;
194 }
195
196 /// Convert a sequence (tipically list) of PySomething to
197 /// OutputList,ParameterList or FeatureList
198 template<typename RET,typename ELEM> //<OutputList> <OutputDescriptor>
199 RET PyTypeInterface::PyValue_To_VampList(PyObject* pyList) const
200 {
201 // Vamp::Plugin::OutputList list;
202 // Vamp::Plugin::OutputDescriptor od;
203 RET list;
204 ELEM element;
205
206 // Type checking
207 if (! PyList_Check(pyList) ) {
208 Py_CLEAR(pyList);
209 // cerr << "ERROR: In Python plugin [" << m_class << "::" << method
210 // << "] Expected List return type." << endl;
211 return list;
212 }
213
214 //This reference will be borrowed
215 PyObject *pyDict;
216
217 //Parse Output List
218 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
219 //Get i-th Vamp output descriptor (Borrowed Reference)
220 pyDict = PyList_GET_ITEM(pyList,i);
221 element = PyValue_To_VampDescriptor<ELEM>(pyDict);
222 // Check for empty Feature/Descriptor as before?
223 list.push_back(element);
224 }
225 return list;
226 }
227
228
229 //Vamp specific types
230
231 Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
232 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
233 { r = this->PyValue_To_FeatureSet(pyValue); }
234
235 Vamp::RealTime::RealTime PyValue_To_RealTime(PyObject*) const;
236 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime::RealTime &r) const
237 { r = this->PyValue_To_RealTime(pyValue); }
238
239
240 /* Overloaded PyValue_To_rValue() to support generic functions */
241 inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const
242 { float tmp = this->PyValue_To_Float(pyValue);
243 if(!m_error) defValue = tmp; }
244 inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const
245 { size_t tmp = this->PyValue_To_Size_t(pyValue);
246 if(!m_error) defValue = tmp; }
247 inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
248 { bool tmp = this->PyValue_To_Bool(pyValue);
249 if(!m_error) defValue = tmp; }
250 inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const
251 { std::string tmp = this->PyValue_To_String(pyValue);
252 if(!m_error) defValue = tmp; }
253 /*used by templates where we expect no return value, if there is one it will be ignored*/
254 inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
255 { if (m_strict && pyValue != Py_None)
256 setValueError("Strict conversion error: expected 'None' type.",m_strict);
257 }
258
259 /* convert sequence types to Vamp List types */
260 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const
261 { r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); }
262 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const
263 { r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); }
264 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const
265 { r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); }
266
267 /// this is only needed for RealTime->Frame conversion
268 void setInputSampleRate(float inputSampleRate)
269 { m_inputSampleRate = (unsigned int) inputSampleRate; }
270
271 private:
272 bool m_strict;
273 ValueError m_noError;
274 mutable bool m_error;
275 mutable ValueError& m_lastError;
276 mutable std::queue<ValueError> m_errorQueue;
277 // we only use it for RealTime conversion which requires unsigned int
278 unsigned int m_inputSampleRate;
279
280 void setValueError(std::string,bool) const;
281 ValueError& _lastError() const;
282
283 /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */
284 inline void _convert(PyObject *pyValue,float &r) const
285 { r = PyValue_To_Float(pyValue); }
286 inline void _convert(PyObject *pyValue,size_t &r) const
287 { r = PyValue_To_Size_t(pyValue); }
288 inline void _convert(PyObject *pyValue,bool &r) const
289 { r = PyValue_To_Bool(pyValue); }
290 inline void _convert(PyObject *pyValue,std::string &r) const
291 { r = PyValue_To_String(pyValue); }
292 inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const
293 { r = PyValue_To_StringVector(pyValue); }
294 inline void _convert(PyObject *pyValue,std::vector<float> &r) const
295 { r = PyValue_To_FloatVector(pyValue); }
296 inline void _convert(PyObject *pyValue,Vamp::RealTime::RealTime &r) const
297 { r = PyValue_To_RealTime(pyValue); }
298
299 public:
300 const bool& error;
301
302 };
303
304 #endif