def _imperative_invoke(handle, ndargs, keys, vals, out):
    """ctypes implementation of imperative invoke wrapper"""
    if out is not None:
        original_output = out
        if isinstance(out, NDArrayBase):
            out = (out,)
        num_output = ctypes.c_int(len(out))
        output_vars = c_handle_array(out)
        output_vars = ctypes.cast(output_vars, ctypes.POINTER(NDArrayHandle))
    else:
        original_output = None
        output_vars = ctypes.POINTER(NDArrayHandle)()
        num_output = ctypes.c_int(0)

    # return output stypes to avoid the c_api call for checking
    # a handle's stype in _ndarray_cls
    out_stypes = ctypes.POINTER(ctypes.c_int)()

    check_call(_LIB.MXImperativeInvokeEx(
        ctypes.c_void_p(handle),
        ctypes.c_int(len(ndargs)),
        c_handle_array(ndargs),
        ctypes.byref(num_output),
        ctypes.byref(output_vars),
        ctypes.c_int(len(keys)),
        c_str_array(keys),
        c_str_array([str(s) for s in vals]),
        ctypes.byref(out_stypes)))

    if original_output is not None:
        return original_output
    if num_output.value == 1:
        return _ndarray_cls(ctypes.cast(output_vars[0], NDArrayHandle),
                            stype=out_stypes[0])
    else:
        return [_ndarray_cls(ctypes.cast(output_vars[i], NDArrayHandle),
                             stype=out_stypes[i])
                for i in range(num_output.value)]
ctypes implementation of imperative invoke wrapper