Home > Blockchain >  loop through a pointer with ctypes
loop through a pointer with ctypes

Time:03-05

I need to use an existing library in my python application. This is a library to read a particular data file. If you are curious you can download it from here enter image description here

But how do I get all the values like in the C sample code?
I'm not a C coder and I don't fully understand what is going on in lines like os->dim[dim].float64[row] It looks like it's looping through memory.
How do I do that in python?

I hope I gave enough details. Thank you in advance.

CodePudding user response:

Here's a working example that accesses the structures mentioned with comments about corrections to the structure definitions and a port of the C code walking the structure. There is test code I used to verify the accesses were written correctly.

test.py

import ctypes as ct

SIE_OUTPUT_FLOAT64 = 1
SIE_OUTPUT_RAW = 2

class sie_Output_Raw(ct.Structure):
    _fields_ = [('ptr', ct.c_void_p),
                ('size', ct.c_size_t),      # better to use matching type for portability
                ('reserved_1', ct.c_int)]

class sie_Output_Dim(ct.Structure):
    _fields_ = [('type', ct.c_int),
                # This is a pointer type, and c_float is 32-bit so use c_double.
                # Also fixed structure names to match C.
                ('float64', ct.POINTER(ct.c_double)),
                # Use pointer to structure type since it is known instead of c_void_p.
                ('raw', ct.POINTER(sie_Output_Raw))]

class sie_Output_Struct(ct.Structure):
    _fields_ = [('num_dims', ct.c_size_t),
                ('num_rows', ct.c_size_t),
                ('reserved_1', ct.c_size_t),
                ('reserved_2', ct.c_size_t),
                ('dim', ct.POINTER(sie_Output_Dim))] # use defined structure pointer

# CDLL/WinDLL are the same on 64-bit, but WinDLL is for 32-bit C function
# using __stdcall calling convention.
# I made a test library for demonstration. See C code below.
hllDll = ct.CDLL(r'./test')

hllDll.sie_output_get_struct.argtypes = ct.c_void_p,
# use defined structure pointer
hllDll.sie_output_get_struct.restype = ct.POINTER(sie_Output_Struct)

os = hllDll.sie_output_get_struct(None)  # no need for from_address now

# port of the C code to access the structure.
# Use .contents to dereference a pointer and access the structure.
for row in range(os.contents.num_rows):
    for dim in range(os.contents.num_dims):
        if dim != 0:
            print(', ', end='')
        if os.contents.dim[dim].type == SIE_OUTPUT_FLOAT64:
            print(f'{os.contents.dim[dim].float64[row]:.15g}', end='')
        else:
            # Equivalent to casting a C 'void*' to 'unsigned char*'
            uchar_p = ct.cast(os.contents.dim[dim].raw[row].ptr, ct.POINTER(ct.c_ubyte))
            size = os.contents.dim[dim].raw[row].size
            if size > 16:
                print(f'(raw data of size {size}.)', end='')
            else:
                # Slicing a pointer to a particular size creates
                # creates a sized list of the array items the
                # pointer points to.  Result passed to bytes
                # for display.
                print(bytes(uchar_p[:size]), end='')
    print()

test.c - Test code with a hard-coded 2x2 output.

#include <stdlib.h>

typedef double sie_float64;
typedef unsigned int sie_uint32;

typedef void sie_Output;

#define SIE_OUTPUT_NONE    0
#define SIE_OUTPUT_FLOAT64 1
#define SIE_OUTPUT_RAW     2

typedef struct _sie_Output_Raw {
    void *ptr;
    size_t size;
    int reserved_1;
} sie_Output_Raw;

typedef struct _sie_Output_Dim {
    int type;
    sie_float64 *float64;
    sie_Output_Raw *raw;
} sie_Output_Dim;

typedef struct _sie_Output_Struct {
    size_t num_dims;
    size_t num_rows;
    size_t reserved_1;
    size_t reserved_2;
    sie_Output_Dim *dim;
} sie_Output_Struct;

__declspec(dllexport)
sie_Output_Struct* sie_output_get_struct(sie_Output* output) {
    sie_Output_Struct* p = malloc(sizeof(sie_Output_Struct));
    p->num_dims = 2;
    p->num_rows = 2;
    p->dim = malloc(2 * sizeof(sie_Output_Dim));
    p->dim[0].type = SIE_OUTPUT_FLOAT64;
    p->dim[0].float64 = malloc(2 * sizeof(sie_float64));
    p->dim[0].raw = NULL;
    p->dim[0].float64[0] = 1.5;
    p->dim[0].float64[1] = 3.75;
    p->dim[1].type = SIE_OUTPUT_RAW;
    p->dim[1].float64 = NULL;
    p->dim[1].raw = malloc(2 * sizeof(sie_Output_Raw));
    p->dim[1].raw[0].ptr = "hello";
    p->dim[1].raw[0].size = 5;
    p->dim[1].raw[1].ptr = "there";
    p->dim[1].raw[1].size = 5;
    return p;
}

Output:

1.5, b'hello'
3.75, b'there'
  • Related