diff PyTypeInterface.h @ 71:40a01bb24209 vampyhost

Pull apart some type conversion classes for possible use in VamPy Host
author Chris Cannam
date Thu, 20 Nov 2014 13:02:50 +0000
parents 5664fe298af2
children ffaa1fb3d7de
line wrap: on
line diff
--- a/PyTypeInterface.h	Mon Nov 17 14:07:00 2014 +0000
+++ b/PyTypeInterface.h	Thu Nov 20 13:02:50 2014 +0000
@@ -12,7 +12,7 @@
 
 /*
 PyTypeInterface: Type safe conversion utilities between Python types 
-and basic C/C++ types and Vamp API types.
+and Vamp API types. See PyTypeConversions for basic C/C++ types.
 */
 
 #ifndef _PY_TYPE_INTERFACE_H_
@@ -25,6 +25,7 @@
 #include "numpy/arrayobject.h"
 #endif
 #include "PyExtensionModule.h"
+#include "PyTypeConversions.h"
 #include <vector>
 #include <queue>
 #include <string>
@@ -34,13 +35,6 @@
 using std::cerr;
 using std::endl;
 
-#ifdef HAVE_NUMPY
-enum eArrayDataType {
-	dtype_float32 = (int) NPY_FLOAT,
-	dtype_complex64 = (int) NPY_CFLOAT 
-	};
-#endif 
-
 namespace o {
 enum eOutDescriptors {
 	not_found,
@@ -95,79 +89,31 @@
 	label
 	};
 
-/* C++ mapping of PyNone Type */
-struct NoneType {};
-
 class PyTypeInterface
 {
+	PyTypeConversions m_conv;
+	
 public:
 	PyTypeInterface();
 	~PyTypeInterface();
 	
-	// Data
- 	class ValueError
-	{
-	public:
-		ValueError() {}
-		ValueError(std::string m, bool s) : message(m),strict(s) {}
-		std::string location;
-		std::string message;
-		bool strict;
-		std::string str() const { 
-			return (location.empty()) ? message : message + "\nLocation: " + location;}
-		void print() const { cerr << str() << endl; }
-		template<typename V> ValueError &operator<< (const V& v)
-		{
-			std::ostringstream ss;
-			ss << v;
-			location += ss.str();
-			return *this;
-		}
-	};
-	
 	// Utilities
-	void setStrictTypingFlag(bool b) {m_strict = b;}
-	void setNumpyInstalled(bool b) {m_numpyInstalled = b;}
+	void setStrictTypingFlag(bool b) {m_strict = b; m_conv.setStrictTypingFlag(b);}
+	void setNumpyInstalled(bool b) {m_numpyInstalled = b; m_conv.setNumpyInstalled(b); }
 	ValueError getError() const;
 	std::string PyValue_Get_TypeName(PyObject*) const;
 	bool initMaps() const;
 
-	// Basic type conversion: Python to C++ 
-	float 	PyValue_To_Float(PyObject*) const;
-	size_t 	PyValue_To_Size_t(PyObject*) const;
-	bool 	PyValue_To_Bool(PyObject*) const;
-	std::string PyValue_To_String(PyObject*) const;
-	long 	PyValue_To_Long(PyObject*) const;
-	// int 	PyValue_To_Int(PyObject* pyValue) const;
-	
-	
-	// C++ to Python
-	PyObject *PyValue_From_CValue(const char*) const;
-	PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); }
-	PyObject *PyValue_From_CValue(size_t) const;
-	PyObject *PyValue_From_CValue(double) const;
-	PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); }
-	PyObject *PyValue_From_CValue(bool) const;
-	
-	// Sequence types
-	std::vector<std::string> PyValue_To_StringVector (PyObject*) const;
-	std::vector<float> PyValue_To_FloatVector (PyObject*) const;
-	std::vector<float> PyList_To_FloatVector (PyObject*) const;
-
 	// Input buffers to Python
 	PyObject* InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
 	PyObject* InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
 
 	// Numpy types
 #ifdef HAVE_NUMPY
-	std::vector<float> PyArray_To_FloatVector (PyObject *pyValue) const;
 	PyObject* InputBuffers_As_NumpyArray(const float *const *inputBuffers, const size_t&, const size_t&, const Vamp::Plugin::InputDomain& dtype);
 #endif
 
-	
-
-
-/* 						Template functions 							*/
+/* Template functions */
 
 
 	/// Common wrappers to set values in Vamp API structs. (to be used in template functions)
@@ -211,9 +157,9 @@
 		//Python Dictionary Iterator:
 		while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
 		{
-			std::string key = PyValue_To_String(pyKey);
+			std::string key = m_conv.PyValue_To_String(pyKey);
 #ifdef _DEBUG_VALUES			
-			cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl;
+			cerr << "key: '" << key << "' value: '" << m_conv.PyValue_To_String(pyDictValue) << "' " << endl;
 #endif			
 			SetValue(rd,key,pyDictValue);
 			if (m_error) {
@@ -296,54 +242,6 @@
 
 	}
 
-	/// Convert DTYPE type 1D NumpyArray to std::vector<RET>
-	template<typename RET, typename DTYPE>
-	std::vector<RET> PyArray_Convert(void* raw_data_ptr, long length, size_t strides) const
-	{
-		std::vector<RET> rValue;
-		
-		/// check if the array is continuous, if not use strides info
-		if (sizeof(DTYPE)!=strides) {
-#ifdef _DEBUG_VALUES
-			cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl;
-#endif
-			char* data = (char*) raw_data_ptr;
-			for (long i = 0; i<length; ++i){
-				rValue.push_back((RET)(*((DTYPE*)data)));
-#ifdef _DEBUG_VALUES
-				cerr << "value: " << (RET)(*((DTYPE*)data)) << endl;
-#endif				
-				data+=strides;
-			}
-			return rValue;
-		}
-
-		DTYPE* data = (DTYPE*) raw_data_ptr;
-		for (long i = 0; i<length; ++i){
-#ifdef _DEBUG_VALUES
-			cerr << "value: " << (RET)data[i] << endl;
-#endif
-			rValue.push_back((RET)data[i]);
-		}
-		return rValue;
-	}
-
-	/// this is a special case. numpy.float64 has an array interface but no array descriptor
-	inline std::vector<float> PyArray0D_Convert(PyArrayInterface *ai) const
-	{
-		std::vector<float> rValue;
-		if ((ai->typekind) == *"f") 
-			rValue.push_back((float)*(double*)(ai->data));
-		else { 
-			setValueError("Unsupported NumPy data type.",m_strict); 
-			return rValue;
-		}
-#ifdef _DEBUG_VALUES
-		cerr << "value: " << rValue[0] << endl;
-#endif
-		return rValue;
-	}
-
 	//Vamp specific types
 	Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
@@ -359,26 +257,25 @@
 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::InputDomain &r) const
 		{ r = this->PyValue_To_InputDomain(pyValue); }
 	
-	
 	/* Overloaded PyValue_To_rValue() to support generic functions */
 	inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const 
-		{ float tmp = this->PyValue_To_Float(pyValue);                                              
+		{ float tmp = m_conv.PyValue_To_Float(pyValue);                                              
 			if(!m_error) defValue = tmp; }
 	inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const
-		{ size_t tmp = this->PyValue_To_Size_t(pyValue); 
+		{ size_t tmp = m_conv.PyValue_To_Size_t(pyValue); 
 			if(!m_error) defValue = tmp; }
 	inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
-		{ bool tmp = this->PyValue_To_Bool(pyValue); 
+		{ bool tmp = m_conv.PyValue_To_Bool(pyValue); 
 			if(!m_error) defValue = tmp; }
 	inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const
-		{ std::string tmp = this->PyValue_To_String(pyValue); 
+		{ std::string tmp = m_conv.PyValue_To_String(pyValue); 
 			if(!m_error) defValue = tmp; }
 	/*used by templates where we expect no return value, if there is one it will be ignored*/			
 	inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
 		{ if (m_strict && pyValue != Py_None) 
 				setValueError("Strict conversion error: Expected 'None' type.",m_strict); 
 		}
-
+	
 	/* convert sequence types to Vamp List types */			
 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const
 		{ r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); }
@@ -403,23 +300,23 @@
 
 	/* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */
 	inline void _convert(PyObject *pyValue,float &r) const 
-		{ r = PyValue_To_Float(pyValue); }
+		{ r = m_conv.PyValue_To_Float(pyValue); }
 	inline void _convert(PyObject *pyValue,size_t &r) const 
-		{ r = PyValue_To_Size_t(pyValue); }
-    inline void _convert(PyObject *pyValue,bool &r) const 
-		{ r = PyValue_To_Bool(pyValue); }
+		{ r = m_conv.PyValue_To_Size_t(pyValue); }
+	inline void _convert(PyObject *pyValue,bool &r) const 
+		{ r = m_conv.PyValue_To_Bool(pyValue); }
 	inline void _convert(PyObject *pyValue,std::string &r) const
-		{ r = PyValue_To_String(pyValue); }
+		{ r = m_conv.PyValue_To_String(pyValue); }
 	inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const
-		{ r = PyValue_To_StringVector(pyValue); }
+		{ r = m_conv.PyValue_To_StringVector(pyValue); }
 	inline void _convert(PyObject *pyValue,std::vector<float> &r) const
-		{ r = PyValue_To_FloatVector(pyValue); }
-    inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const 
+		{ r = m_conv.PyValue_To_FloatVector(pyValue); }
+	inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const 
 		{ r = PyValue_To_RealTime(pyValue); }
 	inline void _convert(PyObject *pyValue,Vamp::Plugin::OutputDescriptor::SampleType &r) const 
 		{ r = PyValue_To_SampleType(pyValue); }
 	// inline void _convert(PyObject *pyValue,Vamp::Plugin::InputDomain &r) const 
-	// 	{ r = PyValue_To_InputDomain(pyValue); }
+	// 	{ r = m_conv.PyValue_To_InputDomain(pyValue); }
 	    
 
 	/* Identify descriptors for error reporting */
@@ -435,246 +332,4 @@
 
 };
 
-/* 		   		  Convert Sample Buffers to Python 	         		*/
-
-/// passing the sample buffers as builtin python lists
-/// Optimization: using fast sequence protocol
-inline PyObject*
-PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
-{
-	//create a list of lists (new references)
-	PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
-	
-	// Pack samples into a Python List Object
-	// pyFloat/pyComplex types will always be new references, 
-	// they will be freed when the lists are deallocated.
-	
-	PyObject **pyChannelListArray =  PySequence_Fast_ITEMS(pyChannelList);
-	for (size_t i=0; i < channels; ++i) {
-		
-        size_t arraySize;
-		if (dtype==Vamp::Plugin::FrequencyDomain) 
-			arraySize = (blockSize / 2) + 1; //blockSize + 2; if cplx list isn't used
-		else 
-			arraySize = blockSize;
-
-		PyObject *pySampleList = PyList_New((Py_ssize_t) arraySize);
-		PyObject **pySampleListArray =  PySequence_Fast_ITEMS(pySampleList);
-		
-		// Note: passing a complex list crashes the C-style plugin
-		// when it tries to convert it to a numpy array directly.
-		// This plugin will be obsolete, but we have to find a way
-		// to prevent such crash: possibly a numpy bug, 
-		// works fine above 1.0.4
-		
-		switch (dtype) //(Vamp::Plugin::TimeDomain)
-		{
-			case Vamp::Plugin::TimeDomain :
-
-			for (size_t j = 0; j < arraySize; ++j) {
-				PyObject *pyFloat=PyFloat_FromDouble(
-					(double) inputBuffers[i][j]);
-				pySampleListArray[j] = pyFloat;
-			}
-			break;
-
-			case Vamp::Plugin::FrequencyDomain :
-
-			size_t k = 0;
-			for (size_t j = 0; j < arraySize; ++j) {
-				PyObject *pyComplex=PyComplex_FromDoubles(
-					(double) inputBuffers[i][k], 
-					(double) inputBuffers[i][k+1]);
-				pySampleListArray[j] = pyComplex;
-				k += 2;
-			}
-			break;
-			
-		}
-		pyChannelListArray[i] = pySampleList;
-	}
-	return pyChannelList;
-}
-
-/// numpy buffer interface: passing the sample buffers as shared memory buffers
-/// Optimization: using sequence protocol for creating the buffer list
-inline PyObject*
-PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
-{	
-	//create a list of buffers (returns new references)
-	PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
-	PyObject **pyChannelListArray =  PySequence_Fast_ITEMS(pyChannelList);
-
-	// Expose memory using the Buffer Interface.		
-	// This will pass a pointer which can be recasted in Python code 
-	// as complex or float array using Numpy's frombuffer() method
-	// (this will not copy values just keep the starting adresses 
-	// for each channel in a list)
-	Py_ssize_t bufferSize;
-	
-	if (dtype==Vamp::Plugin::FrequencyDomain) 
-		bufferSize = (Py_ssize_t) sizeof(float) * (blockSize+2);
-	else 
-		bufferSize = (Py_ssize_t) sizeof(float) * blockSize;
-	
-	for (size_t i=0; i < channels; ++i) {
-		PyObject *pyBuffer = PyBuffer_FromMemory
-		((void *) (float *) inputBuffers[i],bufferSize);
-		pyChannelListArray[i] = pyBuffer;
-	}
-	return pyChannelList;
-}
-
-
-/// numpy array interface: passing the sample buffers as 2D numpy array
-/// Optimization: using array API (needs numpy headers)
-#ifdef HAVE_NUMPY
-inline PyObject*
-PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
-{	
-/*
-NOTE: We create a list of 1D Numpy arrays for each channel instead
-of a matrix, because the address space of inputBuffers doesn't seem
-to be continuous. Although the array strides could be calculated for
-2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure
-if this can be trusted, especially for more than 2 channels.
-
-	cerr << "First channel: " << inputBuffers[0][0] << " address: " <<  inputBuffers[0] << endl;
-	if (channels == 2)
-		cerr << "Second channel: " << inputBuffers[1][0] << " address: " <<  inputBuffers[1] << endl;
-
-*/	
-	
-	// create a list of arrays (returns new references)
-	PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
-	PyObject **pyChannelListArray =  PySequence_Fast_ITEMS(pyChannelList);
-	
-	// Expose memory using the Numpy Array Interface.		
-	// This will wrap an array objects around the data.
-	// (will not copy values just steal the starting adresses)
-
-	int arraySize, typenum;
-	
-	switch (dtype)
-	{
-		case Vamp::Plugin::TimeDomain :
-		typenum = dtype_float32; //NPY_FLOAT; 
-		arraySize = (int) blockSize;
-		break;
-
-		case Vamp::Plugin::FrequencyDomain :
-		typenum = dtype_complex64; //NPY_CFLOAT;
-		arraySize = (int) (blockSize / 2) + 1;
-		break;
-		
-		default :
-		cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl;
-		return pyChannelList;
-	}
-
-	// size for each dimension
-	npy_intp ndims[1]={arraySize}; 
-	
-	for (size_t i=0; i < channels; ++i) {
-		PyObject *pyChannelArray = 
-			//args: (dimensions, size in each dim, type kind, pointer to continuous array)
-			PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]);
-		// make it read-only: set all flags to false except NPY_C_CONTIGUOUS
-		//!!! what about NPY_ARRAY_OWNDATA?
-		PyArray_CLEARFLAGS((PyArrayObject *)pyChannelArray, 0xff);
-		PyArray_ENABLEFLAGS((PyArrayObject *)pyChannelArray, NPY_ARRAY_C_CONTIGUOUS);
-		pyChannelListArray[i] = pyChannelArray;
-	}
-	return pyChannelList;
-}
 #endif
-
-
-
-#ifdef NUMPY_REFERENCE
-/// This should be all we need to compile without direct dependency,
-/// but we don't do that. (it may not work on some platforms)
-typedef struct {
-    int two;              /* contains the integer 2 -- simple sanity check */
-    int nd;               /* number of dimensions */
-    char typekind;        /* kind in array --- character code of typestr */
-    int itemsize;         /* size of each element */
-    int flags;            /* flags indicating how the data should be interpreted */
-                          /*   must set ARR_HAS_DESCR bit to validate descr */
-    Py_intptr_t *shape;   /* A length-nd array of shape information */
-    Py_intptr_t *strides; /* A length-nd array of stride information */
-    void *data;           /* A pointer to the first element of the array */
-    PyObject *descr;      /* NULL or data-description (same as descr key */
-                          /*        of __array_interface__) -- must set ARR_HAS_DESCR */
-                          /*        flag or this will be ignored. */
-} PyArrayInterface;
-
-typedef struct PyArrayObject {
-        PyObject_HEAD
-        char *data;             /* pointer to raw data buffer */
-        int nd;                 /* number of dimensions, also called ndim */
-        npy_intp *dimensions;       /* size in each dimension */
-        npy_intp *strides;          /* bytes to jump to get to the
-                                   next element in each dimension */
-        PyObject *base;         /* This object should be decref'd
-                                   upon deletion of array */
-                                /* For views it points to the original array */
-                                /* For creation from buffer object it points
-                                   to an object that shold be decref'd on
-                                   deletion */
-                                /* For UPDATEIFCOPY flag this is an array
-                                   to-be-updated upon deletion of this one */
-        PyArray_Descr *descr;   /* Pointer to type structure */
-        int flags;              /* Flags describing array -- see below*/
-        PyObject *weakreflist;  /* For weakreferences */
-} PyArrayObject;
-
-typedef struct _PyArray_Descr {
-        PyObject_HEAD
-        PyTypeObject *typeobj;  /* the type object representing an
-                                   instance of this type -- should not
-                                   be two type_numbers with the same type
-                                   object. */
-        char kind;              /* kind for this type */
-        char type;              /* unique-character representing this type */
-        char byteorder;         /* '>' (big), '<' (little), '|'
-                                   (not-applicable), or '=' (native). */
-        char hasobject;         /* non-zero if it has object arrays
-                                   in fields */
-        int type_num;          /* number representing this type */
-        int elsize;             /* element size for this type */
-        int alignment;          /* alignment needed for this type */
-        struct _arr_descr                                       \
-        *subarray;              /* Non-NULL if this type is
-                                   is an array (C-contiguous)
-                                   of some other type
-                                */
-        PyObject *fields;       /* The fields dictionary for this type */
-                                /* For statically defined descr this
-                                   is always Py_None */
-
-        PyObject *names;        /* An ordered tuple of field names or NULL
-                                   if no fields are defined */
-
-        PyArray_ArrFuncs *f;     /* a table of functions specific for each
-                                    basic data descriptor */
-} PyArray_Descr;
-
-enum NPY_TYPES {    NPY_BOOL=0,
-                    NPY_BYTE, NPY_UBYTE,
-                    NPY_SHORT, NPY_USHORT,
-                    NPY_INT, NPY_UINT,
-                    NPY_LONG, NPY_ULONG,
-                    NPY_LONGLONG, NPY_ULONGLONG,
-                    NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE,
-                    NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE,
-                    NPY_OBJECT=17,
-                    NPY_STRING, NPY_UNICODE,
-                    NPY_VOID,
-                    NPY_NTYPES,
-                    NPY_NOTYPE,
-                    NPY_CHAR,      /* special flag */
-                    NPY_USERDEF=256  /* leave room for characters */
-};
-#endif /*NUMPY_REFERENCE*/
-#endif