Ccall, NULL pointers to arrays and sizeof

ccall

#1

I am trying to wrap the Vimba C API (for grabbing data from machine vision cameras). I’m struggling with one aspect (it may be that it is my lack of C knowledge).

The function I’m currently wrapping is declared roughly as:

VmbError_t VmbCamerasList ( VmbCameraInfo_t* pCameraInfo,
                            VmbUint32_t listLength,
                            VmbUint32_t*       pNumFound,
                            VmbUint32_t        sizeofCameraInfo );

First we are supposed to call this function with a NULL pointer for pCameraInfo to retreive the numer of cameras in the 3rd argument. i.e.

ncameras = Ref{VmbUint32_t}(0)
err = ccall((:VmbCamerasList,vmblib), VmbError_t,
            (Ptr{VmbCameraInfo_t}, VmbUint32_t, Ref{VmbUint32_t}, VmbUint32_t),
            C_NULL, 0, ncameras, sizeof(Ptr{VmbCameraInfo_t}))

My first question is what I should be doing with the last argument? The example in the API docs uses:

VmbCameraInfo_t     *pCameras  = NULL;  // A list of camera details
VmbUint32_t         nCount              // Number of found cameras
err = VmbCamerasList( NULL, 0, &nCount, sizeof *pCameras );

What is the Julia equivalent of sizeof *pCameras ? As far as I know, in C this just returns the size of the pointer type, not the size of VmbCameraInfo_t let alone the array it points to.

Subsequently I am supposed to call with a correctly allocated array. The C example is:

pCameras = (VmbCameraInfo_t*) malloc( nCount * sizeof( *pCameras ));
if ( NULL != pCameras ) {
    err = VmbCamerasList( pCameras, nCount, &nFoundCount, sizeof *pCameras ); }

which I translated as

cameras = Array{VmbCameraInfo_t}(ncameras[])
err = ccall((:VmbCamerasList,vmblib), VmbError_t,
            (Ref{VmbCameraInfo_t}, VmbUint32_t, Ref{VmbUint32_t}, VmbUint32_t),
            cameras, ncameras[], ncameras, sizeof(Ptr{VmbCameraInfo_t}))

does that make sense? I’m still confused about the last argument.

Also note that in the first call I declared the first argument to be Ptr{VmbCameraInfo_t} becasue I wanted to pass C_NULL, whereas in the second I declared it to be Ref{VmbHandle_t} so I can directly pass Array{VmbCameraInfo_t}(ncameras[]). Do I need to worry about the C_NULL or does Ref do the right thing here?


#2

It should return the size of the object pointed to by pCameras so assuming pCameras is of type VmbCameraInfo_t* it should be sizeof(VmbCameraInfo_t). Without reading the doc a directly translation should be passing sizeof(VmbCameraInfo_t)

Looks right.

What you are doing should be fine.


#3

Thanks!

I have another question (on the same API). I need to pass the C struct:

typedef struct
{
    const char*     cameraIdString;         # Unique identifier for each camera
    const char*     cameraName;             # Name of the camera
    const char*     modelName;              # Model name
    const char*     serialString;           # Serial number
    VmbAccessMode_t permittedAccess;        # Used access mode, see VmbAccessModeType
    const char*     interfaceIdString;      # Unique value for each interface or bus
} VmbCameraInfo_t;

So I made a Julia type

immutable CameraInfo
    cameraIdString::Ptr{Uint8}          # Unique identifier for each camera
    cameraName::Ptr{Uint8}              # Name of the camera
    modelName::Ptr{Uint8}               # Model name
    serialString::Ptr{Uint8}            # Serial number
    permittedAccessVmbAccessMode_t      # Used access mode, see VmbAccessModeType
    interfaceIdString::Ptr{Uint8}       # Unique value for each interface or bus
end

Now I can load these with the function on my previous post. But as far as I understand I need to unsafe_load all of these Ptr{Uint8} fields after the function call right? As although the structure itself is allocated in Julia (as a member of an Array) the const char* fields are allocated in the C function.

Is there a simple and clean way to load all of these fields safely? Or do I need to make another type with String fields and unsafe_load them across?


#4

unsafe_load is for loading a single element. We do have functions to convert a pointer into a string/array. unsafe_string should copy the NUL-terminated C string into a julia string. unsafe_wrap will create a string or array that shares the pointer. You should pick one of these depending on the memory management of the C library. The doc for these functions should help you pick.

It cannot be done “safely” as the operation of accessing bare memory is unsafe, i.e. we cannot guarantee the safeness.

Also depend on the usecase, If you want to access them repeatedly as julia String object then yes you need to convert it into a different object. You can also lazy convert them with access functions if that’s more efficient for your usecase. Here’re some examples using the first approach from my package.