view PyTypeInterface.h @ 31:4f1894c7591b vampy2

Created Vampy2 branch
author fazekasgy
date Sun, 20 Sep 2009 17:31:20 +0000
parents
children a8231788216c
line wrap: on
line source
/*

Type safe conversion utilities from Python types to C/C++ types,
mainly using Py/C API macros.

*/

#ifndef _PY_TYPE_INTERFACE_H_
#define _PY_TYPE_INTERFACE_H_

#include "vamp-sdk/Plugin.h"
#include <Python.h>
#include "PyExtensionModule.h"
#include <vector>
#include <queue>
#include <string>
//#include <typeinfo>


using std::cerr;
using std::endl;

namespace o {
enum eOutDescriptors {
	not_found,
	identifier,
	name,
	description,
	unit, 
	hasFixedBinCount,
	binCount,
	binNames,
	hasKnownExtents,
	minValue,
	maxValue,
	isQuantized,
	quantizeStep,
	sampleType,	
	sampleRate,
	hasDuration,
	endNode
	}; 
}

namespace p {
enum eParmDescriptors {
	not_found,
	identifier,
	name,
	description,
	unit, 
	minValue,
	maxValue,
	defaultValue,
	isQuantized,
	quantizeStep
	};
}

enum eSampleTypes {
	OneSamplePerStep,
	FixedSampleRate,
	VariableSampleRate
	};

enum eFeatureFields {
	unknown,
	hasTimestamp,
	timeStamp,
	hasDuration,
	duration,
	values,
	label
	};


/// sutructure of NumPy array interface:
/// this is all we need to support numpy without direct dependency
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;

/* C++ mapping of PyNone Type*/
typedef struct NoneType {};

class PyTypeInterface
{
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 get() const { return message + "\nLocation: " + location + "\n";}
		void print() const { cerr << get(); }
	};
	
	// Utilities
	void setStrictTypingFlag(bool b) {m_strict = b;}
	const ValueError &lastError() const;
	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;
	// int PyValue_To_Int(PyObject*) 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;

	// Numpy types
	float* getNumPyObjectData(PyObject *object, int &length) const; 
	

/* 						Template functions 							*/


	/// Common wrappers to set a value in one of these structs. (to be used in template functions)
	void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
	void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
	bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
    PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const 
	{
		if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue);
		if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue);
		if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue);
		return NULL;
	}
	

	template<typename RET> 
	RET PyTypeInterface::PyValue_To_VampDescriptor(PyObject* pyValue) const
	//returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature
	{
		PyObject* pyDict;

		// Descriptors encoded as dicts
		pyDict = GetDescriptor_As_Dict(pyValue);
		if (!pyDict) pyDict = pyValue;
	
		// TODO: support full mapping protocol as fallback.
		if (!PyDict_Check(pyDict)) {
			setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict);
			return RET();
		}

		Py_ssize_t pyPos = 0;
		PyObject *pyKey, *pyDictValue;
		initMaps();
		RET rd;

		//Python Dictionary Iterator:
		while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
		{
			std::string key = PyValue_To_String(pyKey);
			SetValue(rd,key,pyDictValue);
			if (m_error) {
				_lastError().location += "parameter: '" + key + "'";//"' descriptor: '" + rd.identifier + "'";
			}
		}
		if (!m_errorQueue.empty()) m_error = true;
		return rd;
	}

	/// Convert a sequence (tipically list) of PySomething to 
	/// OutputList,ParameterList or FeatureList
	template<typename RET,typename ELEM> //<OutputList> <OutputDescriptor>
	RET PyTypeInterface::PyValue_To_VampList(PyObject* pyList) const
	{
		// Vamp::Plugin::OutputList list;
		// Vamp::Plugin::OutputDescriptor od;
		RET list;
		ELEM element;

		// Type checking
		if (! PyList_Check(pyList) ) {
			Py_CLEAR(pyList);
			// cerr << "ERROR: In Python plugin [" << m_class << "::" << method 
			// << "] Expected List return type." << endl;
			return list;
		}

		//This reference will be borrowed
		PyObject *pyDict;

		//Parse Output List
		for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
			//Get i-th Vamp output descriptor (Borrowed Reference)
			pyDict = PyList_GET_ITEM(pyList,i);
			element = PyValue_To_VampDescriptor<ELEM>(pyDict);
			// Check for empty Feature/Descriptor as before?
			list.push_back(element);
		}
		return list;
	}

	
	//Vamp specific types

	Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
		{ r = this->PyValue_To_FeatureSet(pyValue); }

	Vamp::RealTime::RealTime PyValue_To_RealTime(PyObject*) const;
	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime::RealTime &r) const
		{ r = this->PyValue_To_RealTime(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);                                              
			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); 
			if(!m_error) defValue = tmp; }
	inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
		{ bool tmp = this->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); 
			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); }
	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const
		{ r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); }
	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const
		{ r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); }
	
	/// this is only needed for RealTime->Frame conversion
	void setInputSampleRate(float inputSampleRate)
		{ m_inputSampleRate = (unsigned int) inputSampleRate; }
	
private:
	bool m_strict;
	ValueError m_noError;
	mutable bool m_error;
	mutable ValueError& m_lastError;
	mutable std::queue<ValueError> m_errorQueue;
	// we only use it for RealTime conversion which requires unsigned int
	unsigned int m_inputSampleRate; 
	
	void setValueError(std::string,bool) const;
	ValueError& _lastError() const;

	/* 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); }
	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); }
	inline void _convert(PyObject *pyValue,std::string &r) const
		{ r = PyValue_To_String(pyValue); }
	inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const
		{ r = 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::RealTime &r) const 
		{ r = PyValue_To_RealTime(pyValue); }
	
public:
	const bool& error;

};

#endif