Chris@66
|
1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
|
fazekasgy@37
|
2 /*
|
fazekasgy@37
|
3
|
fazekasgy@37
|
4 * Vampy : This plugin is a wrapper around the Vamp plugin API.
|
fazekasgy@37
|
5 * It allows for writing Vamp plugins in Python.
|
fazekasgy@37
|
6
|
fazekasgy@37
|
7 * Centre for Digital Music, Queen Mary University of London.
|
fazekasgy@37
|
8 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
|
fazekasgy@37
|
9 * for licence information.)
|
fazekasgy@37
|
10
|
fazekasgy@37
|
11 */
|
fazekasgy@37
|
12
|
fazekasgy@37
|
13 #include <Python.h>
|
fazekasgy@37
|
14
|
fazekasgy@37
|
15 #ifdef HAVE_NUMPY
|
fazekasgy@51
|
16
|
fazekasgy@51
|
17 // define a unique API pointer
|
Chris@66
|
18 #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API
|
Chris@66
|
19 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
fazekasgy@37
|
20 #include "numpy/arrayobject.h"
|
fazekasgy@51
|
21
|
fazekasgy@51
|
22 // prevent building with very old versions of numpy
|
fazekasgy@51
|
23 #ifndef NPY_VERSION
|
fazekasgy@51
|
24 #undef HAVE_NUMPY
|
fazekasgy@51
|
25 #endif
|
fazekasgy@51
|
26
|
fazekasgy@51
|
27 #endif
|
fazekasgy@51
|
28
|
fazekasgy@51
|
29 // this is not part of the API, but we will require it for a bug workaround
|
fazekasgy@51
|
30 // define this symbol if you use another version of numpy in the makefile
|
fazekasgy@51
|
31 // Vampy will not attempt to load a lower version than specified
|
fazekasgy@51
|
32 #ifdef HAVE_NUMPY
|
Chris@81
|
33 #ifndef NUMPY_MAJORVERSION
|
Chris@81
|
34 #define NUMPY_MAJORVERSION 1
|
fazekasgy@51
|
35 #endif
|
Chris@81
|
36 #ifndef NUMPY_MINORVERSION
|
Chris@81
|
37 #define NUMPY_MINORVERSION 9
|
Chris@81
|
38 #endif
|
Chris@81
|
39
|
fazekasgy@37
|
40 #endif
|
fazekasgy@37
|
41
|
Chris@120
|
42 #ifdef _WIN32
|
Chris@120
|
43 // We should be using the unicode apis, but we're not (yet)
|
Chris@120
|
44 #undef UNICODE
|
Chris@120
|
45 #undef _UNICODE
|
Chris@120
|
46 #define _MBCS 1
|
Chris@120
|
47 #endif
|
Chris@120
|
48
|
fazekasgy@37
|
49 #include "vamp/vamp.h"
|
fazekasgy@37
|
50 #include "vamp-sdk/PluginAdapter.h"
|
fazekasgy@37
|
51 #include "PyPlugScanner.h"
|
fazekasgy@37
|
52 #include "PyPlugin.h"
|
fazekasgy@37
|
53 #include "PyExtensionModule.h"
|
fazekasgy@37
|
54 #include "PyExtensionManager.h"
|
Chris@67
|
55 #include "Debug.h"
|
gyorgyf@63
|
56 #include <sstream>
|
fazekasgy@37
|
57
|
fazekasgy@37
|
58 #ifdef _WIN32
|
fazekasgy@37
|
59 #define pathsep ('\\')
|
fazekasgy@37
|
60 #include <windows.h>
|
fazekasgy@37
|
61 #include <tchar.h>
|
fazekasgy@37
|
62 #else
|
fazekasgy@37
|
63 #define pathsep ('/')
|
fazekasgy@37
|
64 #include <dirent.h>
|
fazekasgy@37
|
65 #include <dlfcn.h>
|
fazekasgy@37
|
66 #endif
|
fazekasgy@37
|
67
|
Chris@116
|
68 #include "version.h"
|
Chris@116
|
69
|
fazekasgy@37
|
70 using std::cerr;
|
fazekasgy@37
|
71 using std::endl;
|
fazekasgy@37
|
72 using std::string;
|
fazekasgy@37
|
73 using std::vector;
|
fazekasgy@37
|
74
|
fazekasgy@37
|
75 static int adinstcount;
|
fazekasgy@37
|
76 static int totinstcount;
|
fazekasgy@51
|
77 static bool numpyInstalled = false;
|
fazekasgy@51
|
78 static bool arrayApiInitialised = false;
|
fazekasgy@37
|
79
|
fazekasgy@37
|
80 class PyPluginAdapter : public Vamp::PluginAdapterBase
|
fazekasgy@37
|
81 {
|
fazekasgy@37
|
82 public:
|
fazekasgy@37
|
83 PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) :
|
fazekasgy@37
|
84 PluginAdapterBase(),
|
fazekasgy@37
|
85 m_plug(pyPlugId),
|
fazekasgy@37
|
86 m_pyClass(pyClass),
|
fazekasgy@37
|
87 m_failed(false)
|
fazekasgy@37
|
88 {
|
Chris@67
|
89 DSTREAM << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl;
|
fazekasgy@37
|
90 adinstcount++;
|
fazekasgy@37
|
91 }
|
fazekasgy@37
|
92
|
fazekasgy@37
|
93 ~PyPluginAdapter()
|
fazekasgy@37
|
94 {
|
fazekasgy@37
|
95 }
|
fazekasgy@37
|
96
|
fazekasgy@37
|
97 bool failed() { return m_failed; }
|
fazekasgy@37
|
98 std::string getPlugKey() { return m_plug; }
|
fazekasgy@37
|
99
|
fazekasgy@37
|
100 protected:
|
fazekasgy@37
|
101 Vamp::Plugin *createPlugin(float inputSampleRate)
|
fazekasgy@37
|
102 {
|
fazekasgy@37
|
103 try {
|
fazekasgy@51
|
104 PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass, totinstcount, numpyInstalled);
|
fazekasgy@37
|
105 return plugin;
|
fazekasgy@37
|
106 } catch (...) {
|
Chris@67
|
107 cerr << "ERROR: PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl;
|
Chris@67
|
108 // any plugin with syntax errors will fail to construct
|
Chris@67
|
109 m_failed = true;
|
fazekasgy@37
|
110 return 0;
|
fazekasgy@37
|
111 }
|
fazekasgy@37
|
112 }
|
fazekasgy@37
|
113
|
fazekasgy@37
|
114 std::string m_plug;
|
fazekasgy@37
|
115 PyObject *m_pyClass;
|
fazekasgy@37
|
116 bool m_failed;
|
fazekasgy@37
|
117 };
|
fazekasgy@37
|
118
|
fazekasgy@51
|
119
|
fazekasgy@37
|
120 static void array_API_initialiser()
|
fazekasgy@37
|
121 {
|
fazekasgy@51
|
122 if (arrayApiInitialised) return;
|
fazekasgy@51
|
123
|
fazekasgy@51
|
124 /* Numpy 1.3 build note: there seems to be a bug
|
fazekasgy@51
|
125 in this version (at least on OS/X) which will cause memory
|
fazekasgy@51
|
126 access error in the array API import function if an earlier runtime
|
fazekasgy@51
|
127 version of Numpy is used when loading the library.
|
fazekasgy@51
|
128 (below is a horrible workaround)
|
fazekasgy@51
|
129 */
|
fazekasgy@51
|
130
|
fazekasgy@37
|
131 #ifdef HAVE_NUMPY
|
fazekasgy@51
|
132
|
Chris@81
|
133 string ver, majorver, minorver;
|
gyorgyf@63
|
134 std::istringstream verStream;
|
Chris@81
|
135 int numpyVersionMajor, numpyVersionMinor;
|
fazekasgy@51
|
136
|
fazekasgy@51
|
137 /// attmept to test numpy version before importing the array API
|
Chris@67
|
138 DSTREAM << "Numpy build information: ABI level: " << NPY_VERSION
|
Chris@81
|
139 << " Numpy version: " << NUMPY_MAJORVERSION << "." << NUMPY_MINORVERSION << endl;
|
fazekasgy@51
|
140
|
fazekasgy@51
|
141 PyObject *pyModule, *pyDict, *pyVer;
|
fazekasgy@51
|
142
|
fazekasgy@51
|
143 pyModule = PyImport_ImportModule("numpy"); //numpy.core.multiarray
|
fazekasgy@51
|
144 if (!pyModule) {
|
Chris@67
|
145 cerr << "ERROR: Vampy was compiled with Numpy support but Numpy does not seem to be installed." << endl;
|
fazekasgy@51
|
146 #ifdef __APPLE__
|
fazekasgy@51
|
147 cerr << "Hint: Check if Numpy is installed for the particular setup of Python used by Vampy (given by Python exec prefix)." << endl;
|
fazekasgy@51
|
148 #endif
|
fazekasgy@51
|
149 goto numpyFailure;
|
fazekasgy@51
|
150 }
|
fazekasgy@51
|
151
|
fazekasgy@51
|
152 pyDict = PyModule_GetDict(pyModule); // borrowed ref
|
fazekasgy@51
|
153 if (!pyDict) {
|
Chris@67
|
154 cerr << "ERROR: Can not access Numpy module dictionary." << endl;
|
fazekasgy@51
|
155 goto numpyFailure;
|
fazekasgy@51
|
156 }
|
fazekasgy@51
|
157
|
fazekasgy@51
|
158 pyVer = PyDict_GetItemString(pyDict,"__version__"); //borrowed ref
|
fazekasgy@51
|
159 if (!pyVer) {
|
Chris@67
|
160 cerr << "ERROR: Can not access Numpy version information." << endl;
|
fazekasgy@51
|
161 goto numpyFailure;
|
fazekasgy@51
|
162 }
|
fazekasgy@51
|
163
|
fazekasgy@51
|
164 ver = PyString_AsString(pyVer);
|
fazekasgy@51
|
165 ver = ver.substr(0,ver.rfind("."));
|
Chris@81
|
166 majorver = ver.substr(0,ver.rfind("."));
|
Chris@81
|
167 minorver = ver.substr(ver.rfind(".")+1);
|
Chris@81
|
168
|
Chris@81
|
169 // parse version string to float
|
Chris@81
|
170 verStream.str(majorver);
|
Chris@81
|
171 verStream >> numpyVersionMajor;
|
Chris@81
|
172 verStream.str(minorver);
|
Chris@81
|
173 verStream >> numpyVersionMinor;
|
Chris@67
|
174
|
Chris@81
|
175 DSTREAM << "Numpy runtime version: " << numpyVersionMajor << "." << numpyVersionMinor << endl;
|
Chris@81
|
176
|
Chris@81
|
177 if(numpyVersionMajor < NUMPY_MAJORVERSION ||
|
Chris@81
|
178 (numpyVersionMajor < NUMPY_MAJORVERSION &&
|
Chris@81
|
179 numpyVersionMinor < NUMPY_MINORVERSION)) {
|
Chris@81
|
180 cerr << "ERROR: Incompatible Numpy version found: " << ver << endl;
|
fazekasgy@51
|
181 goto numpyFailure;
|
fazekasgy@51
|
182 }
|
fazekasgy@51
|
183
|
fazekasgy@51
|
184 Py_DECREF(pyModule);
|
fazekasgy@51
|
185
|
fazekasgy@51
|
186 // At least we catch import errors, but if binary compatibility
|
fazekasgy@51
|
187 // has changed without notice, this would still fail.
|
fazekasgy@51
|
188 // However, we should never get to this point now anyway.
|
fazekasgy@37
|
189 import_array();
|
fazekasgy@51
|
190 if (PyErr_Occurred()) {
|
Chris@67
|
191 cerr << "ERROR: Import error while loading the Numpy Array API." << endl;
|
fazekasgy@51
|
192 PyErr_Print(); PyErr_Clear();
|
fazekasgy@51
|
193 goto numpyFailure;
|
fazekasgy@51
|
194 }
|
fazekasgy@51
|
195 else {
|
fazekasgy@51
|
196 numpyInstalled = true;
|
fazekasgy@51
|
197 arrayApiInitialised = true;
|
fazekasgy@51
|
198 return;
|
fazekasgy@51
|
199 }
|
fazekasgy@51
|
200
|
fazekasgy@51
|
201
|
fazekasgy@51
|
202 numpyFailure:
|
Chris@81
|
203 cerr << "Please make sure you have Numpy " << NUMPY_MAJORVERSION << "." << NUMPY_MINORVERSION << " or greater installed." << endl;
|
fazekasgy@51
|
204 cerr << "Vampy: Numpy support disabled." << endl;
|
fazekasgy@51
|
205 numpyInstalled = false;
|
fazekasgy@51
|
206 arrayApiInitialised = true;
|
fazekasgy@51
|
207 if (pyModule) Py_XDECREF(pyModule);
|
fazekasgy@51
|
208 return;
|
fazekasgy@51
|
209
|
fazekasgy@51
|
210 /*HAVE_NUMPY*/
|
Chris@116
|
211 #else
|
Chris@116
|
212 DSTREAM << "Numpy support deselected at compile time, not attempting to initialise it" << endl;
|
Chris@116
|
213 #endif
|
fazekasgy@51
|
214
|
Chris@116
|
215 numpyInstalled = false;
|
fazekasgy@51
|
216 arrayApiInitialised = true;
|
fazekasgy@51
|
217 return;
|
fazekasgy@37
|
218 }
|
fazekasgy@37
|
219
|
fazekasgy@37
|
220
|
fazekasgy@37
|
221 static std::vector<PyPluginAdapter *> adapters;
|
fazekasgy@37
|
222 static bool haveScannedPlugins = false;
|
fazekasgy@37
|
223
|
fazekasgy@37
|
224 static bool tryPreload(string name)
|
fazekasgy@37
|
225 {
|
cannam@53
|
226 // cerr << "tryPreload: " << name << endl;
|
fazekasgy@37
|
227 #ifdef _WIN32
|
fazekasgy@37
|
228 void *lib = LoadLibrary(name.c_str());
|
fazekasgy@37
|
229 if (!lib) {
|
fazekasgy@37
|
230 return false;
|
fazekasgy@37
|
231 }
|
fazekasgy@37
|
232 #else
|
fazekasgy@37
|
233 void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
fazekasgy@37
|
234 if (!lib) {
|
cannam@56
|
235 //perror("dlopen");
|
fazekasgy@37
|
236 return false;
|
fazekasgy@37
|
237 }
|
fazekasgy@37
|
238 #endif
|
Chris@67
|
239 DSTREAM << "Preloaded Python from " << name << endl;
|
fazekasgy@37
|
240 return true;
|
fazekasgy@37
|
241 }
|
fazekasgy@37
|
242
|
fazekasgy@37
|
243 static bool preloadPython()
|
fazekasgy@37
|
244 {
|
fazekasgy@37
|
245 #ifdef _WIN32
|
fazekasgy@37
|
246 // this doesn't seem to be necessary at all on Windows
|
fazekasgy@37
|
247 return true;
|
fazekasgy@37
|
248 #endif
|
fazekasgy@37
|
249
|
fazekasgy@37
|
250 string pyver = Py_GetVersion();
|
fazekasgy@37
|
251 int dots = 2;
|
fazekasgy@37
|
252 string shortver;
|
fazekasgy@37
|
253 for (size_t i = 0; i < pyver.length(); ++i) {
|
fazekasgy@37
|
254 if (pyver[i] == '.') {
|
fazekasgy@37
|
255 if (--dots == 0) {
|
fazekasgy@37
|
256 shortver = pyver.substr(0, i);
|
fazekasgy@37
|
257 break;
|
fazekasgy@37
|
258 }
|
fazekasgy@37
|
259 }
|
fazekasgy@37
|
260 }
|
Chris@67
|
261 DSTREAM << "Short version: " << shortver << endl;
|
Chris@67
|
262 // this is useful to find out where the loaded library might be loaded from
|
Chris@67
|
263 DSTREAM << "Python exec prefix: " << Py_GetExecPrefix() << endl;
|
fazekasgy@37
|
264
|
cannam@55
|
265 char *pylib = getenv("VAMPY_PYLIB");
|
cannam@54
|
266 if (pylib && *pylib) {
|
Chris@67
|
267 DSTREAM << "Trying to preload Python from specified location " << pylib
|
Chris@67
|
268 << "..." << endl;
|
cannam@54
|
269 return tryPreload(string(pylib));
|
cannam@54
|
270 }
|
cannam@54
|
271
|
fazekasgy@37
|
272 vector<string> pfxs;
|
cannam@53
|
273 pfxs.push_back(string(Py_GetExecPrefix()) + "/");
|
cannam@53
|
274 pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/");
|
fazekasgy@37
|
275 pfxs.push_back("");
|
fazekasgy@37
|
276 pfxs.push_back("/usr/lib/");
|
fazekasgy@37
|
277 pfxs.push_back("/usr/local/lib/");
|
fazekasgy@37
|
278 char buffer[5];
|
fazekasgy@37
|
279
|
fazekasgy@37
|
280 // hahaha! grossness is like a brother to us
|
fazekasgy@37
|
281 #ifdef __APPLE__
|
fazekasgy@37
|
282 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
|
cannam@53
|
283 // cerr << "prefix: " << pfxs[pfxidx] << endl;
|
cannam@53
|
284 if (tryPreload(pfxs[pfxidx] + string("Python"))) return true;
|
fazekasgy@37
|
285 for (int minor = 8; minor >= 0; --minor) {
|
fazekasgy@37
|
286 sprintf(buffer, "%d", minor);
|
fazekasgy@37
|
287 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true;
|
fazekasgy@37
|
288 }
|
fazekasgy@37
|
289 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true;
|
fazekasgy@37
|
290 if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true;
|
fazekasgy@37
|
291 }
|
fazekasgy@37
|
292 #else
|
fazekasgy@37
|
293 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
|
fazekasgy@37
|
294 for (int minor = 8; minor >= 0; --minor) {
|
fazekasgy@37
|
295 sprintf(buffer, "%d", minor);
|
fazekasgy@37
|
296 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true;
|
fazekasgy@37
|
297 }
|
fazekasgy@37
|
298 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true;
|
fazekasgy@37
|
299 if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true;
|
fazekasgy@37
|
300 }
|
fazekasgy@37
|
301 #endif
|
fazekasgy@37
|
302
|
fazekasgy@37
|
303 return false;
|
fazekasgy@37
|
304 }
|
fazekasgy@37
|
305
|
fazekasgy@37
|
306
|
fazekasgy@37
|
307 static PyExtensionManager pyExtensionManager;
|
fazekasgy@37
|
308
|
fazekasgy@37
|
309 const VampPluginDescriptor
|
fazekasgy@37
|
310 *vampGetPluginDescriptor(unsigned int version,unsigned int index)
|
Chris@79
|
311 {
|
Chris@79
|
312 DSTREAM << "# vampGetPluginDescriptor(" << version << "," << index << ")" << endl;
|
Chris@79
|
313
|
Chris@116
|
314 if (version < 1) return 0;
|
fazekasgy@37
|
315
|
Chris@116
|
316 DSTREAM << "# Vampy version: " << VAMPY_VERSION << endl;
|
Chris@116
|
317
|
fazekasgy@37
|
318 int isPythonInitialized = Py_IsInitialized();
|
Chris@67
|
319 DSTREAM << "# isPythonInitialized: " << isPythonInitialized << endl;
|
Chris@67
|
320 DSTREAM << "# haveScannedPlugins: " << haveScannedPlugins << endl;
|
fazekasgy@37
|
321
|
fazekasgy@37
|
322 if (!haveScannedPlugins) {
|
fazekasgy@37
|
323
|
fazekasgy@37
|
324 if (!isPythonInitialized){
|
fazekasgy@37
|
325
|
fazekasgy@37
|
326 if (!preloadPython())
|
fazekasgy@37
|
327 cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl;
|
fazekasgy@37
|
328 if (PyImport_AppendInittab("vampy",initvampy) != 0)
|
fazekasgy@37
|
329 cerr << "Warning: Extension module could not be added to module inittab." << endl;
|
fazekasgy@37
|
330 Py_Initialize();
|
fazekasgy@51
|
331 array_API_initialiser();
|
fazekasgy@37
|
332 initvampy();
|
Chris@67
|
333 DSTREAM << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl;
|
fazekasgy@37
|
334 }
|
fazekasgy@37
|
335
|
fazekasgy@37
|
336 vector<string> pyPlugs;
|
fazekasgy@37
|
337 vector<string> pyPath;
|
fazekasgy@37
|
338 vector<PyObject *> pyClasses;
|
fazekasgy@37
|
339 static PyPlugScanner *scanner;
|
fazekasgy@37
|
340
|
fazekasgy@37
|
341 //Scanning Plugins
|
Chris@67
|
342 DSTREAM << "Scanning Vampy Plugins" << endl;
|
fazekasgy@37
|
343 scanner = PyPlugScanner::getInstance();
|
fazekasgy@37
|
344
|
fazekasgy@37
|
345 // added env. varable support VAMPY_EXTPATH
|
fazekasgy@37
|
346 pyPath=scanner->getAllValidPath();
|
fazekasgy@37
|
347 scanner->setPath(pyPath);
|
fazekasgy@37
|
348
|
fazekasgy@37
|
349 // added env. variable support:
|
fazekasgy@37
|
350 // VAMPY_COMPILED=1 to recognise .pyc files (default is 1)
|
fazekasgy@37
|
351 pyPlugs = scanner->getPyPlugs();
|
fazekasgy@37
|
352
|
Chris@67
|
353 DSTREAM << "Found " << pyPlugs.size() << " Scripts." << endl;
|
fazekasgy@37
|
354 //TODO: should this support multiple classes per script (?)
|
fazekasgy@37
|
355 pyClasses = scanner->getPyClasses();
|
Chris@67
|
356 DSTREAM << "Found " << pyClasses.size() << " Classes." << endl;
|
fazekasgy@37
|
357
|
fazekasgy@37
|
358 for (size_t i = 0; i < pyPlugs.size(); ++i) {
|
fazekasgy@37
|
359 adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i]));
|
fazekasgy@37
|
360 }
|
fazekasgy@37
|
361 pyExtensionManager.setPlugModuleNames(pyPlugs);
|
fazekasgy@37
|
362 pyExtensionManager.initExtension();
|
fazekasgy@37
|
363 array_API_initialiser();
|
fazekasgy@37
|
364 haveScannedPlugins=true;
|
fazekasgy@37
|
365 }
|
fazekasgy@37
|
366
|
Chris@67
|
367 DSTREAM << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl;
|
fazekasgy@37
|
368
|
Chris@84
|
369 while (index < adapters.size()) {
|
fazekasgy@37
|
370
|
fazekasgy@37
|
371 const VampPluginDescriptor *tmp = adapters[index]->getDescriptor();
|
fazekasgy@37
|
372
|
fazekasgy@37
|
373 if (adapters[index]->failed()) {
|
fazekasgy@37
|
374 cerr << "\nERROR: [in vampGetPluginDescriptor] Removing adapter of: \n'"
|
Chris@87
|
375 << adapters[index]->getPlugKey() << "'\n"
|
Chris@87
|
376 << "The plugin has failed to construct. Hint: Check __init__() function." << endl;
|
fazekasgy@37
|
377 pyExtensionManager.deleteModuleName(adapters[index]->getPlugKey());
|
fazekasgy@37
|
378 delete adapters[index];
|
Chris@87
|
379 adapters.erase(adapters.begin() + index);
|
Chris@84
|
380 continue;
|
fazekasgy@37
|
381 }
|
fazekasgy@37
|
382
|
fazekasgy@37
|
383 return tmp;
|
Chris@84
|
384 }
|
fazekasgy@37
|
385
|
Chris@84
|
386 return 0;
|
fazekasgy@37
|
387 }
|
fazekasgy@37
|
388
|
fazekasgy@37
|
389
|
fazekasgy@37
|
390
|
fazekasgy@37
|
391
|
fazekasgy@37
|
392
|
fazekasgy@37
|
393
|
fazekasgy@37
|
394
|
fazekasgy@37
|
395
|