Create ufunc for struct array

This change allows ufuncs to be registered for structured arrays. For example, a ufunc could be registered to take two arrays of type 'u8,u8,u8' and return an array of type 'u8,u8,u8'.

The following are the key changes:

1) Add new API method called PyUFunc_RegisterLoopForStructType. This method behaves the same as PyUFunc_RegisterLoopForType, except that instead of taking an array of type nums for the ufunc operands, it takes an array of struct dtype objects.

2) Add arg_dtypes array to PyUFunc_Loop1d struct. This stores the dtype objects passed in through call to RegisterLoopForStructType.

3) Add nargs int value to PyUFunc_Loop1d struct. This stores the number of input and output arguments to ufunc, so that dtype objects in PyUFunc_Loop1d struct can be DECREFed when PyUFUnc_Loop1d struct is destroyed.

4) Add logic in ufunc_loop_matches function that checks struct dtype object in arg_dtypes if current operand being checked is type NPY_VOID. 

The following is a trivial 1-d loop function example written in Cython that adds corresponding fields in two arrays of type 'u8,u8,u8':

```
cdef void add_uint64_triplet(char **args,
    npy_intp *dimensions,
    npy_intp *steps,
    void *innerloopdata):
   
    cdef npy_intp i
    cdef npy_intp is1=steps[0]
    cdef npy_intp is2=steps[1]
    cdef npy_intp os=steps[2]
    cdef npy_intp n=dimensions[0]
    cdef uint64_t *x, *y, *z

    cdef char *i1=args[0], *i2=args[1], *op=args[2]

    for i in range(n):

        # Initialize pointers to each operand
        x = <uint64_t*>i1
        y = <uint64_t*>i2
        z = <uint64_t*>op

        # Add each field in struct array record
        z[0] = x[0] + y[0]
        z[1] = x[1] + y[1]
        z[2] = x[2] + y[2]

        i1 += is1; i2 += is2; op += os


def test_ufunc:
    cdef PyArray_Descr *types[3]
    dtype = np.dtype('u8,u8,u8')

    types[0] = <PyArray_Descr*>dtype
    types[1] = <PyArray_Descr*>dtype
    types[2] = <PyArray_Descr*>dtype

    ufunc = PyUFunc_FromFuncAndData(NULL, NULL, NULL, 0,
                                        2, 1, PyUFunc_None,
                                        NULL, NULL, 0) 

    # Call new method for registering ufunc for struct array
    PyUFunc_RegisterLoopForStructType(<PyUFuncObject*>ufunc,
                                      <PyArray_Descr*>dtype,
                                      &add_uint64_triplet,
                                      <PyArray_Descr**>types,
                                      NULL)

    a = np.array([(1,2,3)], dtype=dtype)
    b = np.array([(1,2,3)], dtype=dtype)

    result = ufunc(a, b)
```