Just create a pointer, assign the data afterwards;
import ctypes
class EmxArray(ctypes.Structure):
""" creates a struct to match emxArray_real_T """
_fields_ = [('data', ctypes.POINTER(ctypes.c_double)),
('size', ctypes.POINTER(ctypes.c_int)),
('allocatedSize', ctypes.c_int),
('numDimensions', ctypes.c_int),
('canFreeData', ctypes.c_bool)]
data = (1.3, 3.5, 2.7, 4.1)
L = len(data)
e = EmxArray()
e.data = (ctypes.c_double * L)(*data)
e.size = (ctypes.c_int * 1)(L)
# et cetera
Answer from Roland Smith on Stack OverflowJust create a pointer, assign the data afterwards;
import ctypes
class EmxArray(ctypes.Structure):
""" creates a struct to match emxArray_real_T """
_fields_ = [('data', ctypes.POINTER(ctypes.c_double)),
('size', ctypes.POINTER(ctypes.c_int)),
('allocatedSize', ctypes.c_int),
('numDimensions', ctypes.c_int),
('canFreeData', ctypes.c_bool)]
data = (1.3, 3.5, 2.7, 4.1)
L = len(data)
e = EmxArray()
e.data = (ctypes.c_double * L)(*data)
e.size = (ctypes.c_int * 1)(L)
# et cetera
I'm not well-versed with the Python-C interface, so what I'm suggesting may be less than ideal. My guess is that likely the crash is because x->data is never initialized and the memory to which it is pointing isn't allocated.
An approach I have taken when interfacing to MATLAB Coder generated code from other languages in the presence of emxArray arguments is to hand-write a C interface function that provides a simpler API. This relieves the burden of needing to construct an emxArray in the other environment (Android Java in my particular case). If the generated function foo takes and returns a 2-D double array, then something like the following could work:
void foo(double *x, int *szx, double **y, int *szy);
This function would take a pointer to the input data and its size and provide a pointer to the output data and its size. The implementation would look something like:
void foo(double *x, int *szx, double **y, int *szy)
{
emxArray_real_T *pEmx;
emxArray_real_T *pEmy;
/* Create input emxArray assuming 2-dimensional input */
pEmx = emxCreateWrapper_real_T(x, szx[0], szx[1]);
/* Create output emxArray (assumes that the output is not */
/* written before allocation occurs) assuming 2-D output */
pEmy = emxCreateWrapper_real_T(NULL, 0, 0);
/* Call generated code (call foobar_initialize/terminate elsewhere) */
foobar(pEmx, pEmy);
/* Unpack result - You may want to MALLOC storage in *y and */
/* MEMCPY there alternatively */
*y = pEmy->data;
szy[0] = pEmy->size[0];
szy[1] = pEmy->size[1];
/* Clean up any memory allocated in the emxArrays (e.g. the size vectors) */
emxDestroyArray_real_T(pEmx);
emxDestroyArray_real_T(pEmy);
}
You should be able to call this function from Python more simply and pass in the desired data as needed.
My other answer has more details on the emxArray_* functions found in the file foobar_emxAPI.h.
Here's a working example. It looks like you are passing the wrong type to the function and the structure was declared incorrectly for the Data member. Note below how to declare the type of a fixed-size array when declaring the Python structure.
test.c ("cl /W4 /LD .c" on Windows):
#include <stdio.h>
#ifdef _WIN32
# define SMBUS_API __declspec(dllexport)
#else
# define SMBUS_API
#endif
#define SMB_MAX_DATA_SIZE 5
typedef void* SMBUS_HANDLE;
typedef struct _SMB_REQUEST {
unsigned char Address;
unsigned char Command;
unsigned char BlockLength;
unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;
SMBUS_API int SmBusReadByte(SMBUS_HANDLE handle, SMB_REQUEST *request) {
for(unsigned char i = 0; i < request->BlockLength; ++i)
request->Data[i] = i;
return request->BlockLength;
}
SMBUS_API SMBUS_HANDLE OpenSmbus(void) {
return (void*)0x12345678;
}
test.py:
import ctypes as ct
SMB_MAX_DATA_SIZE = 5
ARRAY = ct.c_ubyte * SMB_MAX_DATA_SIZE
class SMB_REQUEST(ct.Structure):
_fields_ = (('Address', ct.c_ubyte),
('Command', ct.c_ubyte),
('BlockLength', ct.c_ubyte),
('Data', ARRAY))
dll = ct.CDLL('./test')
smbus_read_byte = dll.SmBusReadByte
smbus_read_byte.argtypes = ct.c_void_p, ct.POINTER(SMB_REQUEST)
smbus_read_byte.restype = ct.c_int
open_smbus = dll.OpenSmbus
open_smbus.argtypes = ()
open_smbus.restype = ct.c_void_p
handle = open_smbus()
print(f'{handle = :08X}h')
smb_request = SMB_REQUEST(1, 2, 5)
print(f'returned = {smbus_read_byte(handle, smb_request)}')
print(f'Address = {smb_request.Address}')
print(f'Command = {smb_request.Command}')
print(f'BlockLength = {smb_request.BlockLength}')
for i, b in enumerate(smb_request.Data):
print(f'Data[{i}] = {b:02X}h')
Output:
handle = 12345678h
returned = 5
Address = 1
Command = 2
BlockLength = 5
Data[0] = 00h
Data[1] = 01h
Data[2] = 02h
Data[3] = 03h
Data[4] = 04h
You're almost there. You should use c_char * SMB_MAX_DATA_SIZE as the type for the definition of Data. This works for me on Mac OS X:
Shared library:
$ cat test.c
#include <stdio.h>
#define SMB_MAX_DATA_SIZE 16
typedef struct _SMB_REQUEST
{
unsigned char Address;
unsigned char Command;
unsigned char BlockLength;
unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;
int SmBusReadByte(void *handle, SMB_REQUEST *request)
{
printf("SmBusReadByte: handle=%p request=[%d %d %d %s]\n", handle,
request->Address, request->Command, request->BlockLength, request->Data);
return 13;
}
$ gcc test.c -fPIC -shared -o libtest.dylib
Python driver:
$ cat test.py
import ctypes
SMB_MAX_DATA_SIZE = 16
class SMB_REQUEST(ctypes.Structure):
_fields_ = [("Address", ctypes.c_ubyte),
("Command", ctypes.c_ubyte),
("BlockLength", ctypes.c_ubyte),
("Data", ctypes.c_char * SMB_MAX_DATA_SIZE)]
libtest = ctypes.cdll.LoadLibrary('libtest.dylib')
req = SMB_REQUEST(1, 2, 3, 'test')
result = libtest.SmBusReadByte(ctypes.c_voidp(0x12345678), ctypes.byref(req))
print 'result: %d' % result
$ python test.py
SmBusReadByte: handle=0x12345678 request=[1 2 3 test]
result: 13
UPDATE
You're having problems because you need to set the result type of open_smbus to void*. By default, ctypes assumes that functions return ints. You need to say this:
open_smbus.restype = ctypes.c_void_p
You were getting an error because you were using c_void_p() (note the extra parentheses). There's an important distinction between c_void_p and c_void_p(). The former is a type, and the latter is an instance of a type. c_void_p represents the C type void*, whereas c_void_p() represents an actual pointer instance (with a default value of 0).