Type selector using template specialization


In an effort to contribute back to the great matplotlib-cpp library I came across the need of having to create a mechanism that can select between different types in C++. This was needed in order to allow passing data from C++ to python without any copies using the numpy library. The only caveat in this case in that numpy requires to know the type you are passing. Since the matplotlib-cpp library is template-based there is a nice compile-time way to do it using template specialization. In simple terms it boils down to creating a base case for all the types you do not support and specialize for the types you care. Using a simple struct to hold the type completes the whole construct. The final result is :

// Type selector for numpy array conversion
template <typename T>
struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default

template <>
struct select_npy_type<double> { const static NPY_TYPES type = NPY_DOUBLE; };

template <>
struct select_npy_type<float> { const static NPY_TYPES type = NPY_FLOAT; };

template <>
struct select_npy_type<bool> { const static NPY_TYPES type = NPY_BOOL; };

template <>
struct select_npy_type<std::int8_t> { const static NPY_TYPES type = NPY_INT8; };

template <>
struct select_npy_type<std::int16_t> { const static NPY_TYPES type = NPY_SHORT; };

template <>
struct select_npy_type<std::int32_t> { const static NPY_TYPES type = NPY_INT; };

template <>
struct select_npy_type<std::int64_t> { const static NPY_TYPES type = NPY_INT64; };

template <>
struct select_npy_type<std::uint8_t> { const static NPY_TYPES type = NPY_UINT8; };

template <>
struct select_npy_type<std::uint16_t> { const static NPY_TYPES type = NPY_USHORT; };

template <>
struct select_npy_type<std::uint32_t> { const static NPY_TYPES type = NPY_ULONG; };

template <>
struct select_npy_type<std::uint64_t> { const static NPY_TYPES type = NPY_UINT64; };

It can be used as follows :

template<typename Numeric>
PyObject* get_array(const std::vector<Numeric>& v)
{
	detail::_interpreter::get();	//interpreter needs to be initialized for the numpy commands to work
	NPY_TYPES type = select_npy_type<Numeric>::type; 
	if (type == NPY_NOTYPE)
	{
		std::vector<double> vd(v.size());
		npy_intp vsize = v.size();
		std::copy(v.begin(),v.end(),vd.begin());
		PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void*)(vd.data()));
		return varray;
	}

	npy_intp vsize = v.size();
	PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data()));
	return varray;
}

Relevant Pull Request