Repository: eigency-org/eigency (language: python)

Issue:
unable to pass empty matrix eigen to numpy
Hello,

A have a code in c++ that compute a variable size vector. then this vector is send using eigency to python.
For some data the algorithm create an empty vector, then when a tried to send this empty vector to python I get the following message:

        return  ndarray(self.dn_cpp.ComputeVectorSometimeEmptyVector())
      File "eigency/conversions.pyx", line 67, in eigency.conversions.ndarray_long_F
    ValueError: Cannot create cython.array from NULL pointer

The problem is that at the cython level I have only a PlainObjectBase, with no possibility to test the size (rows() and cols()) to handle this situation correctly.

Any ideas on how can treat this problem.

Felipe 





Discussion / hints:
What is the return type of `ComputeVectorSometimeEmptyVector`? Can it return a null pointer?
The `eigency.conversions.ndarray_long_F` function assumes that the data pointer is not null.

I'm not sure what you mean by "at the cython level I have only a PlainObjectBase". Where are you getting this PlainObjectBase from?
Confirmed that Eigen has a `null` matrix that does not allocate:
```c++
#include <iostream>
#include <Eigen/Dense>
 
using Eigen::MatrixXd;
 
int main()
{
  MatrixXd m(2,2);
  std::cout << m.data() << std::endl;
  MatrixXd n;
  std::cout << n.data() << std::endl;
}
```

Output:
```
0x1405eb0
0
```

Fix patch:
diff --git a/eigency/conversions.pxd b/eigency/conversions.pxd
index b4138b1..caae863 100644
--- a/eigency/conversions.pxd
+++ b/eigency/conversions.pxd
@@ -1,71 +1,85 @@
 cimport numpy as np
 
 
+cdef api np.ndarray[long double, ndim=2] ndarray_long_double()
 cdef api np.ndarray[long double, ndim=2] ndarray_long_double_C(long double *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[long double, ndim=2] ndarray_long_double_F(long double *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[long double, ndim=2] ndarray_copy_long_double_C(const long double *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[long double, ndim=2] ndarray_copy_long_double_F(const long double *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[double, ndim=2] ndarray_double()
 cdef api np.ndarray[double, ndim=2] ndarray_double_C(double *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[double, ndim=2] ndarray_double_F(double *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[double, ndim=2] ndarray_copy_double_C(const double *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[double, ndim=2] ndarray_copy_double_F(const double *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[float, ndim=2] ndarray_float()
 cdef api np.ndarray[float, ndim=2] ndarray_float_C(float *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[float, ndim=2] ndarray_float_F(float *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[float, ndim=2] ndarray_copy_float_C(const float *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[float, ndim=2] ndarray_copy_float_F(const float *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[long, ndim=2] ndarray_long()
 cdef api np.ndarray[long, ndim=2] ndarray_long_C(long *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[long, ndim=2] ndarray_long_F(long *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[long, ndim=2] ndarray_copy_long_C(const long *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[long, ndim=2] ndarray_copy_long_F(const long *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[unsigned long, ndim=2] ndarray_ulong()
 cdef api np.ndarray[unsigned long, ndim=2] ndarray_ulong_C(unsigned long *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned long, ndim=2] ndarray_ulong_F(unsigned long *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned long, ndim=2] ndarray_copy_ulong_C(const unsigned long *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned long, ndim=2] ndarray_copy_ulong_F(const unsigned long *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[int, ndim=2] ndarray_int()
 cdef api np.ndarray[int, ndim=2] ndarray_int_C(int *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[int, ndim=2] ndarray_int_F(int *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[int, ndim=2] ndarray_copy_int_C(const int *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[int, ndim=2] ndarray_copy_int_F(const int *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[unsigned int, ndim=2] ndarray_uint()
 cdef api np.ndarray[unsigned int, ndim=2] ndarray_uint_C(unsigned int *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned int, ndim=2] ndarray_uint_F(unsigned int *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned int, ndim=2] ndarray_copy_uint_C(const unsigned int *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned int, ndim=2] ndarray_copy_uint_F(const unsigned int *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[short, ndim=2] ndarray_short()
 cdef api np.ndarray[short, ndim=2] ndarray_short_C(short *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[short, ndim=2] ndarray_short_F(short *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[short, ndim=2] ndarray_copy_short_C(const short *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[short, ndim=2] ndarray_copy_short_F(const short *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[unsigned short, ndim=2] ndarray_ushort()
 cdef api np.ndarray[unsigned short, ndim=2] ndarray_ushort_C(unsigned short *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned short, ndim=2] ndarray_ushort_F(unsigned short *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned short, ndim=2] ndarray_copy_ushort_C(const unsigned short *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned short, ndim=2] ndarray_copy_ushort_F(const unsigned short *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[signed char, ndim=2] ndarray_schar()
 cdef api np.ndarray[signed char, ndim=2] ndarray_schar_C(signed char *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[signed char, ndim=2] ndarray_schar_F(signed char *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[signed char, ndim=2] ndarray_copy_schar_C(const signed char *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[signed char, ndim=2] ndarray_copy_schar_F(const signed char *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[unsigned char, ndim=2] ndarray_uchar()
 cdef api np.ndarray[unsigned char, ndim=2] ndarray_uchar_C(unsigned char *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned char, ndim=2] ndarray_uchar_F(unsigned char *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned char, ndim=2] ndarray_copy_uchar_C(const unsigned char *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[unsigned char, ndim=2] ndarray_copy_uchar_F(const unsigned char *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[np.npy_clongdouble, ndim=2] ndarray_complex_long_double()
 cdef api np.ndarray[np.npy_clongdouble, ndim=2] ndarray_complex_long_double_C(np.npy_clongdouble *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.npy_clongdouble, ndim=2] ndarray_complex_long_double_F(np.npy_clongdouble *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.npy_clongdouble, ndim=2] ndarray_copy_complex_long_double_C(const np.npy_clongdouble *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.npy_clongdouble, ndim=2] ndarray_copy_complex_long_double_F(const np.npy_clongdouble *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[np.complex128_t, ndim=2] ndarray_complex_double()
 cdef api np.ndarray[np.complex128_t, ndim=2] ndarray_complex_double_C(np.complex128_t *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.complex128_t, ndim=2] ndarray_complex_double_F(np.complex128_t *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.complex128_t, ndim=2] ndarray_copy_complex_double_C(const np.complex128_t *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.complex128_t, ndim=2] ndarray_copy_complex_double_F(const np.complex128_t *data, long rows, long cols, long outer_stride, long inner_stride)
 
+cdef api np.ndarray[np.complex64_t, ndim=2] ndarray_complex_float()
 cdef api np.ndarray[np.complex64_t, ndim=2] ndarray_complex_float_C(np.complex64_t *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.complex64_t, ndim=2] ndarray_complex_float_F(np.complex64_t *data, long rows, long cols, long outer_stride, long inner_stride)
 cdef api np.ndarray[np.complex64_t, ndim=2] ndarray_copy_complex_float_C(const np.complex64_t *data, long rows, long cols, long outer_stride, long inner_stride)
diff --git a/eigency/conversions.pyx b/eigency/conversions.pyx
index 8667713..d9eb433 100644
--- a/eigency/conversions.pyx
+++ b/eigency/conversions.pyx
@@ -7,6 +7,10 @@ from numpy.lib.stride_tricks import as_strided
 # long double
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[long double, ndim=2] ndarray_long_double():
+    return np.empty((0,0), dtype='longdouble')
+
 @cython.boundscheck(False)
 cdef np.ndarray[long double, ndim=2] ndarray_long_double_C(long double *data, long rows, long cols, long row_stride, long col_stride):
     cdef long double[:,:] mem_view = <long double[:rows,:cols]>data
@@ -39,6 +43,10 @@ cdef np.ndarray[long double, ndim=2] ndarray_copy_long_double_F(const long doubl
 # double
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[double, ndim=2] ndarray_double():
+    return np.empty((0,0), dtype='double')
+
 @cython.boundscheck(False)
 cdef np.ndarray[double, ndim=2] ndarray_double_C(double *data, long rows, long cols, long row_stride, long col_stride):
     cdef double[:,:] mem_view = <double[:rows,:cols]>data
@@ -71,6 +79,10 @@ cdef np.ndarray[double, ndim=2] ndarray_copy_double_F(const double *data, long r
 # float
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[float, ndim=2] ndarray_float():
+    return np.empty((0,0), dtype='float')
+
 @cython.boundscheck(False)
 cdef np.ndarray[float, ndim=2] ndarray_float_C(float *data, long rows, long cols, long row_stride, long col_stride):
     cdef float[:,:] mem_view = <float[:rows,:cols]>data
@@ -103,6 +115,10 @@ cdef np.ndarray[float, ndim=2] ndarray_copy_float_F(const float *data, long rows
 # long
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[long, ndim=2] ndarray_long():
+    return np.empty((0,0), dtype='int_')
+
 @cython.boundscheck(False)
 cdef np.ndarray[long, ndim=2] ndarray_long_C(long *data, long rows, long cols, long row_stride, long col_stride):
     cdef long[:,:] mem_view = <long[:rows,:cols]>data
@@ -135,6 +151,10 @@ cdef np.ndarray[long, ndim=2] ndarray_copy_long_F(const long *data, long rows, l
 # unsigned long
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[unsigned long, ndim=2] ndarray_ulong():
+    return np.empty((0,0), dtype='uint')
+
 @cython.boundscheck(False)
 cdef np.ndarray[unsigned long, ndim=2] ndarray_ulong_C(unsigned long *data, long rows, long cols, long row_stride, long col_stride):
     cdef unsigned long[:,:] mem_view = <unsigned long[:rows,:cols]>data
@@ -167,6 +187,10 @@ cdef np.ndarray[unsigned long, ndim=2] ndarray_copy_ulong_F(const unsigned long
 # int
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[int, ndim=2] ndarray_int():
+    return np.empty((0,0), dtype='int')
+
 @cython.boundscheck(False)
 cdef np.ndarray[int, ndim=2] ndarray_int_C(int *data, long rows, long cols, long row_stride, long col_stride):
     cdef int[:,:] mem_view = <int[:rows,:cols]>data
@@ -199,6 +223,10 @@ cdef np.ndarray[int, ndim=2] ndarray_copy_int_F(const int *data, long rows, long
 # unsigned int
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[unsigned int, ndim=2] ndarray_uint():
+    return np.empty((0,0), dtype='uint')
+
 @cython.boundscheck(False)
 cdef np.ndarray[unsigned int, ndim=2] ndarray_uint_C(unsigned int *data, long rows, long cols, long row_stride, long col_stride):
     cdef unsigned int[:,:] mem_view = <unsigned int[:rows,:cols]>data
@@ -231,6 +259,10 @@ cdef np.ndarray[unsigned int, ndim=2] ndarray_copy_uint_F(const unsigned int *da
 # short
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[short, ndim=2] ndarray_short():
+    return np.empty((0,0), dtype='short')
+
 @cython.boundscheck(False)
 cdef np.ndarray[short, ndim=2] ndarray_short_C(short *data, long rows, long cols, long row_stride, long col_stride):
     cdef short[:,:] mem_view = <short[:rows,:cols]>data
@@ -263,6 +295,10 @@ cdef np.ndarray[short, ndim=2] ndarray_copy_short_F(const short *data, long rows
 # unsigned short
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[unsigned short, ndim=2] ndarray_ushort():
+    return np.empty((0,0), dtype='ushort')
+
 @cython.boundscheck(False)
 cdef np.ndarray[unsigned short, ndim=2] ndarray_ushort_C(unsigned short *data, long rows, long cols, long row_stride, long col_stride):
     cdef unsigned short[:,:] mem_view = <unsigned short[:rows,:cols]>data
@@ -295,6 +331,10 @@ cdef np.ndarray[unsigned short, ndim=2] ndarray_copy_ushort_F(const unsigned sho
 # signed char
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[signed char, ndim=2] ndarray_schar():
+    return np.empty((0,0), dtype='int8')
+
 @cython.boundscheck(False)
 cdef np.ndarray[signed char, ndim=2] ndarray_schar_C(signed char *data, long rows, long cols, long row_stride, long col_stride):
     cdef signed char[:,:] mem_view = <signed char[:rows,:cols]>data
@@ -327,6 +367,10 @@ cdef np.ndarray[signed char, ndim=2] ndarray_copy_schar_F(const signed char *dat
 # unsigned char
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[unsigned char, ndim=2] ndarray_uchar():
+    return np.empty((0,0), dtype='uint8')
+
 @cython.boundscheck(False)
 cdef np.ndarray[unsigned char, ndim=2] ndarray_uchar_C(unsigned char *data, long rows, long cols, long row_stride, long col_stride):
     cdef unsigned char[:,:] mem_view = <unsigned char[:rows,:cols]>data
@@ -359,6 +403,10 @@ cdef np.ndarray[unsigned char, ndim=2] ndarray_copy_uchar_F(const unsigned char
 # complex long double
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[np.npy_clongdouble, ndim=2] ndarray_complex_long_double():
+    return np.empty((0,0), dtype='clongdouble')
+
 @cython.boundscheck(False)
 cdef np.ndarray[np.npy_clongdouble, ndim=2] ndarray_complex_long_double_C(np.npy_clongdouble *data, long rows, long cols, long row_stride, long col_stride):
     cdef np.npy_clongdouble[:,:] mem_view = <np.npy_clongdouble[:rows,:cols]>data
@@ -391,6 +439,10 @@ cdef np.ndarray[np.npy_clongdouble, ndim=2] ndarray_copy_complex_long_double_F(c
 # complex double
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[np.complex128_t, ndim=2] ndarray_complex_double():
+    return np.empty((0,0), dtype='complex128')
+
 @cython.boundscheck(False)
 cdef np.ndarray[np.complex128_t, ndim=2] ndarray_complex_double_C(np.complex128_t *data, long rows, long cols, long row_stride, long col_stride):
     cdef np.complex128_t[:,:] mem_view = <np.complex128_t[:rows,:cols]>data
@@ -423,6 +475,10 @@ cdef np.ndarray[np.complex128_t, ndim=2] ndarray_copy_complex_double_F(const np.
 # complex float
 #
 
+@cython.boundscheck(False)
+cdef np.ndarray[np.complex64_t, ndim=2] ndarray_complex_float():
+    return np.empty((0,0), dtype='complex64')
+
 @cython.boundscheck(False)
 cdef np.ndarray[np.complex64_t, ndim=2] ndarray_complex_float_C(np.complex64_t *data, long rows, long cols, long row_stride, long col_stride):
     cdef np.complex64_t[:,:] mem_view = <np.complex64_t[:rows,:cols]>data
diff --git a/eigency/eigency_cpp.h b/eigency/eigency_cpp.h
index b0474e1..e80013b 100644
--- a/eigency/eigency_cpp.h
+++ b/eigency/eigency_cpp.h
@@ -31,9 +31,11 @@ inline PyArrayObject* _ndarray_copy(const Scalar *, long rows, long cols, bool i
 // Consequently, a switch in numpy storage order from row-major to column-major involves a switch
 // in strides, while it does not affect the stride in Eigen.
 
-#define _NDAV(TYPE, FUNC_NAME_C, FUNC_NAME_F) template<>                                                                                    \
+#define _NDAV(TYPE, FUNC_NAME_E, FUNC_NAME_C, FUNC_NAME_F) template<>                                                                       \
 inline PyArrayObject* _ndarray_view< TYPE >(TYPE *data, long rows, long cols, bool is_row_major, long outer_stride, long inner_stride) {    \
-    if (is_row_major) {                                                                                                                     \
+    if (data == nullptr) {                                                                                                                  \
+        return FUNC_NAME_E();                                                                                                               \
+    } else if (is_row_major) {                                                                                                              \
         /* Eigen row-major mode: row_stride=outer_stride, and col_stride=inner_stride */                                                    \
         /* If no stride is given, the row_stride is set to the number of columns. */                                                        \
         return FUNC_NAME_C(data, rows, cols, outer_stride>0?outer_stride:cols, inner_stride>0?inner_stride:1);                              \
@@ -44,43 +46,45 @@ inline PyArrayObject* _ndarray_view< TYPE >(TYPE *data, long rows, long cols, bo
     }                                                                                                                                       \
 }
 
-#define _NDAC(TYPE, FUNC_NAME_C, FUNC_NAME_F) template<>                                                                                        \
+#define _NDAC(TYPE, FUNC_NAME_E, FUNC_NAME_C, FUNC_NAME_F) template<>                                                                           \
 inline PyArrayObject* _ndarray_copy< TYPE >(const TYPE *data, long rows, long cols, bool is_row_major, long outer_stride, long inner_stride) {  \
-    if (is_row_major) {                                                                                                                         \
+    if (data == nullptr) {                                                                                                                      \
+        return FUNC_NAME_E();                                                                                                                   \
+    } else if (is_row_major) {                                                                                                                  \
         return FUNC_NAME_C(data, rows, cols, outer_stride>0?outer_stride:cols, inner_stride>0?inner_stride:1);                                  \
     } else {                                                                                                                                    \
         return FUNC_NAME_F(data, rows, cols, inner_stride>0?inner_stride:1, outer_stride>0?outer_stride:rows);                                  \
     }                                                                                                                                           \
 }
 
-_NDAV(long double, ndarray_long_double_C, ndarray_long_double_F)
-_NDAC(long double, ndarray_copy_long_double_C, ndarray_copy_long_double_F)
-_NDAV(double, ndarray_double_C, ndarray_double_F)
-_NDAC(double, ndarray_copy_double_C, ndarray_copy_double_F)
-_NDAV(float, ndarray_float_C, ndarray_float_F)
-_NDAC(float, ndarray_copy_float_C, ndarray_copy_float_F)
-_NDAV(long, ndarray_long_C, ndarray_long_F)
-_NDAC(long, ndarray_copy_long_C, ndarray_copy_long_F)
-_NDAV(unsigned long, ndarray_ulong_C, ndarray_ulong_F)
-_NDAC(unsigned long, ndarray_copy_ulong_C, ndarray_copy_ulong_F)
-_NDAV(int, ndarray_int_C, ndarray_int_F)
-_NDAC(int, ndarray_copy_int_C, ndarray_copy_int_F)
-_NDAV(unsigned int, ndarray_uint_C, ndarray_uint_F)
-_NDAC(unsigned int, ndarray_copy_uint_C, ndarray_copy_uint_F)
-_NDAV(short, ndarray_short_C, ndarray_short_F)
-_NDAC(short, ndarray_copy_short_C, ndarray_copy_short_F)
-_NDAV(unsigned short, ndarray_ushort_C, ndarray_ushort_F)
-_NDAC(unsigned short, ndarray_copy_ushort_C, ndarray_copy_ushort_F)
-_NDAV(signed char, ndarray_schar_C, ndarray_schar_F)
-_NDAC(signed char, ndarray_copy_schar_C, ndarray_copy_schar_F)
-_NDAV(unsigned char, ndarray_uchar_C, ndarray_uchar_F)
-_NDAC(unsigned char, ndarray_copy_uchar_C, ndarray_copy_uchar_F)
-_NDAV(npy_clongdouble, ndarray_complex_long_double_C, ndarray_complex_long_double_F)
-_NDAC(npy_clongdouble, ndarray_copy_complex_long_double_C, ndarray_copy_complex_long_double_F)
-_NDAV(std::complex<double>, ndarray_complex_double_C, ndarray_complex_double_F)
-_NDAC(std::complex<double>, ndarray_copy_complex_double_C, ndarray_copy_complex_double_F)
-_NDAV(std::complex<float>, ndarray_complex_float_C, ndarray_complex_float_F)
-_NDAC(std::complex<float>, ndarray_copy_complex_float_C, ndarray_copy_complex_float_F)
+_NDAV(long double, ndarray_long_double, ndarray_long_double_C, ndarray_long_double_F)
+_NDAC(long double, ndarray_long_double, ndarray_copy_long_double_C, ndarray_copy_long_double_F)
+_NDAV(double, ndarray_double, ndarray_double_C, ndarray_double_F)
+_NDAC(double, ndarray_double, ndarray_copy_double_C, ndarray_copy_double_F)
+_NDAV(float, ndarray_float, ndarray_float_C, ndarray_float_F)
+_NDAC(float, ndarray_float, ndarray_copy_float_C, ndarray_copy_float_F)
+_NDAV(long, ndarray_long, ndarray_long_C, ndarray_long_F)
+_NDAC(long, ndarray_long, ndarray_copy_long_C, ndarray_copy_long_F)
+_NDAV(unsigned long, ndarray_ulong, ndarray_ulong_C, ndarray_ulong_F)
+_NDAC(unsigned long, ndarray_ulong, ndarray_copy_ulong_C, ndarray_copy_ulong_F)
+_NDAV(int, ndarray_int, ndarray_int_C, ndarray_int_F)
+_NDAC(int, ndarray_int, ndarray_copy_int_C, ndarray_copy_int_F)
+_NDAV(unsigned int, ndarray_uint, ndarray_uint_C, ndarray_uint_F)
+_NDAC(unsigned int, ndarray_uint, ndarray_copy_uint_C, ndarray_copy_uint_F)
+_NDAV(short, ndarray_short, ndarray_short_C, ndarray_short_F)
+_NDAC(short, ndarray_short, ndarray_copy_short_C, ndarray_copy_short_F)
+_NDAV(unsigned short, ndarray_ushort, ndarray_ushort_C, ndarray_ushort_F)
+_NDAC(unsigned short, ndarray_ushort, ndarray_copy_ushort_C, ndarray_copy_ushort_F)
+_NDAV(signed char, ndarray_schar, ndarray_schar_C, ndarray_schar_F)
+_NDAC(signed char, ndarray_schar, ndarray_copy_schar_C, ndarray_copy_schar_F)
+_NDAV(unsigned char, ndarray_uchar, ndarray_uchar_C, ndarray_uchar_F)
+_NDAC(unsigned char, ndarray_uchar, ndarray_copy_uchar_C, ndarray_copy_uchar_F)
+_NDAV(npy_clongdouble, ndarray_complex_long_double, ndarray_complex_long_double_C, ndarray_complex_long_double_F)
+_NDAC(npy_clongdouble, ndarray_complex_long_double, ndarray_copy_complex_long_double_C, ndarray_copy_complex_long_double_F)
+_NDAV(std::complex<double>, ndarray_complex_double, ndarray_complex_double_C, ndarray_complex_double_F)
+_NDAC(std::complex<double>, ndarray_complex_double, ndarray_copy_complex_double_C, ndarray_copy_complex_double_F)
+_NDAV(std::complex<float>, ndarray_complex_float, ndarray_complex_float_C, ndarray_complex_float_F)
+_NDAC(std::complex<float>, ndarray_complex_float, ndarray_copy_complex_float_C, ndarray_copy_complex_float_F)
 
 #undef _NDAV
 #undef _NDAC


Test patch:
diff --git a/tests/eigency_tests/eigency_tests.pyx b/tests/eigency_tests/eigency_tests.pyx
index d3d6922..1b42461 100644
--- a/tests/eigency_tests/eigency_tests.pyx
+++ b/tests/eigency_tests/eigency_tests.pyx
@@ -34,6 +34,8 @@ cdef extern from "eigency_tests/eigency_tests_cpp.h":
 
      cdef Matrix3d _function_w_mat_retval "function_w_mat_retval" ()
 
+     cdef MatrixXd _function_w_empty_mat_retval "function_w_empty_mat_retval" ()
+
      cdef PlainObjectBase _function_w_mat_retval_full_spec "function_w_mat_retval_full_spec" ()
 
      cdef Map[ArrayXXd] &_function_filter1 "function_filter1" (Map[ArrayXXd] &)
@@ -118,6 +120,10 @@ def function_w_vec_retval():
 def function_w_mat_retval():
     return ndarray(_function_w_mat_retval())
 
+# Function returning empty matrix (copy is made)
+def function_w_empty_mat_retval():
+    return ndarray(_function_w_empty_mat_retval())
+
 # Function returning matrix (copy is made)
 def function_w_mat_retval_full_spec():
     return ndarray(_function_w_mat_retval_full_spec())
diff --git a/tests/eigency_tests/eigency_tests_cpp.cpp b/tests/eigency_tests/eigency_tests_cpp.cpp
index 77cf80b..3193688 100644
--- a/tests/eigency_tests/eigency_tests_cpp.cpp
+++ b/tests/eigency_tests/eigency_tests_cpp.cpp
@@ -53,6 +53,10 @@ Eigen::Matrix3d function_w_mat_retval() {
     return Eigen::Matrix3d::Constant(4.);
 }
 
+Eigen::MatrixXd function_w_empty_mat_retval() {
+    return Eigen::MatrixXd();
+}
+
 Eigen::Matrix<double, 2, 4> function_w_mat_retval_full_spec() {
     return Eigen::Matrix<double, 2, 4>::Constant(2.);
 }
diff --git a/tests/eigency_tests/eigency_tests_cpp.h b/tests/eigency_tests/eigency_tests_cpp.h
index b0e3e39..0d8d29e 100644
--- a/tests/eigency_tests/eigency_tests_cpp.h
+++ b/tests/eigency_tests/eigency_tests_cpp.h
@@ -26,6 +26,8 @@ Eigen::VectorXd function_w_vec_retval();
 
 Eigen::Matrix3d function_w_mat_retval();
 
+Eigen::MatrixXd function_w_empty_mat_retval();
+
 Eigen::Matrix<double, 2, 4> function_w_mat_retval_full_spec();
 
 Eigen::Map<Eigen::ArrayXXd> &function_filter1(Eigen::Map<Eigen::ArrayXXd> &);
diff --git a/tests/run_tests.py b/tests/run_tests.py
index e7e294f..bf07313 100644
--- a/tests/run_tests.py
+++ b/tests/run_tests.py
@@ -77,6 +77,10 @@ def test_mat_retval(self):
         retval = eigency_tests.function_w_mat_retval()
         self.assertAlmostEqual(retval[0, 0], 4.0)
 
+    def test_empty_mat_retval(self):
+        retval = eigency_tests.function_w_empty_mat_retval()
+        self.assertEqual(retval.size, 0)
+
     def test_mat_ref_retval(self):
         my_object = eigency_tests.FixedMatrixClass()
         retval = my_object.get_matrix()