STRUCT_ARRAY in your code should be a pointer to a pointer array, not a pointer to an element in the array, as you are appending STRUCT_2 pointers to the array.

In C, STRUCT_ARRAY can be defined as *STRUCT_2[length], aka an array of STRUCT_2 pointers

import ctypes
from random import randint

class STRUCT_2(ctypes.Structure):
    #_pack_=2
    _fields_ = [('field_1', ctypes.c_short),
                ('field_2', ctypes.c_short),
                ('field_3', ctypes.c_short),
                ('field_4', ctypes.c_short),
                ('field_5', ctypes.c_short),
                ('field_6', ctypes.c_short),
                ('field_7', ctypes.c_short),
                ('field_8', ctypes.c_short)]

class STRUCT_1(ctypes.Structure):
    #_pack_=2
    _fields_ = [('elements', ctypes.c_short),
                #an array of pointers
                ('STRUCT_ARRAY', ctypes.POINTER(ctypes.POINTER(STRUCT_2)))]

    def __init__(self,num_of_structs):
        elems = (ctypes.POINTER(STRUCT_2) * num_of_structs)()
        self.STRUCT_ARRAY = ctypes.cast(elems,ctypes.POINTER(ctypes.POINTER(STRUCT_2)))
        self.elements = num_of_structs

        for num in range(0,num_of_structs):
            self.STRUCT_ARRAY[num].field_1 = 1
            self.STRUCT_ARRAY[num].field_2 = 2
            self.STRUCT_ARRAY[num].field_3 = 3

for num in range(0,100):
    test = STRUCT_1(num)
    print("%i done" % num)

If instead what you want is an array of structs, use the following code:

import ctypes
from random import randint

class STRUCT_2(ctypes.Structure):
    #_pack_=2
    _fields_ = [('field_1', ctypes.c_short),
                ('field_2', ctypes.c_short),
                ('field_3', ctypes.c_short),
                ('field_4', ctypes.c_short),
                ('field_5', ctypes.c_short),
                ('field_6', ctypes.c_short),
                ('field_7', ctypes.c_short),
                ('field_8', ctypes.c_short)]

class STRUCT_1(ctypes.Structure):
    #_pack_=2
    _fields_ = [('elements', ctypes.c_short),
                #an array of structs
                ('STRUCT_ARRAY', ctypes.POINTER(STRUCT_2))]

    def __init__(self,num_of_structs):
        elems = (STRUCT_2 * num_of_structs)()
        self.STRUCT_ARRAY = ctypes.cast(elems,ctypes.POINTER(STRUCT_2))
        self.elements = num_of_structs

        for num in range(0,num_of_structs):
            self.STRUCT_ARRAY[num].field_1 = 1
            self.STRUCT_ARRAY[num].field_2 = 2
            self.STRUCT_ARRAY[num].field_3 = 3
            self.STRUCT_ARRAY[num].field_4 = 4

for num in range(0,100):
    test = STRUCT_1(num)
    print("%i done" % num)
Answer from simonzack on Stack Overflow
Top answer
1 of 1
20

STRUCT_ARRAY in your code should be a pointer to a pointer array, not a pointer to an element in the array, as you are appending STRUCT_2 pointers to the array.

In C, STRUCT_ARRAY can be defined as *STRUCT_2[length], aka an array of STRUCT_2 pointers

import ctypes
from random import randint

class STRUCT_2(ctypes.Structure):
    #_pack_=2
    _fields_ = [('field_1', ctypes.c_short),
                ('field_2', ctypes.c_short),
                ('field_3', ctypes.c_short),
                ('field_4', ctypes.c_short),
                ('field_5', ctypes.c_short),
                ('field_6', ctypes.c_short),
                ('field_7', ctypes.c_short),
                ('field_8', ctypes.c_short)]

class STRUCT_1(ctypes.Structure):
    #_pack_=2
    _fields_ = [('elements', ctypes.c_short),
                #an array of pointers
                ('STRUCT_ARRAY', ctypes.POINTER(ctypes.POINTER(STRUCT_2)))]

    def __init__(self,num_of_structs):
        elems = (ctypes.POINTER(STRUCT_2) * num_of_structs)()
        self.STRUCT_ARRAY = ctypes.cast(elems,ctypes.POINTER(ctypes.POINTER(STRUCT_2)))
        self.elements = num_of_structs

        for num in range(0,num_of_structs):
            self.STRUCT_ARRAY[num].field_1 = 1
            self.STRUCT_ARRAY[num].field_2 = 2
            self.STRUCT_ARRAY[num].field_3 = 3

for num in range(0,100):
    test = STRUCT_1(num)
    print("%i done" % num)

If instead what you want is an array of structs, use the following code:

import ctypes
from random import randint

class STRUCT_2(ctypes.Structure):
    #_pack_=2
    _fields_ = [('field_1', ctypes.c_short),
                ('field_2', ctypes.c_short),
                ('field_3', ctypes.c_short),
                ('field_4', ctypes.c_short),
                ('field_5', ctypes.c_short),
                ('field_6', ctypes.c_short),
                ('field_7', ctypes.c_short),
                ('field_8', ctypes.c_short)]

class STRUCT_1(ctypes.Structure):
    #_pack_=2
    _fields_ = [('elements', ctypes.c_short),
                #an array of structs
                ('STRUCT_ARRAY', ctypes.POINTER(STRUCT_2))]

    def __init__(self,num_of_structs):
        elems = (STRUCT_2 * num_of_structs)()
        self.STRUCT_ARRAY = ctypes.cast(elems,ctypes.POINTER(STRUCT_2))
        self.elements = num_of_structs

        for num in range(0,num_of_structs):
            self.STRUCT_ARRAY[num].field_1 = 1
            self.STRUCT_ARRAY[num].field_2 = 2
            self.STRUCT_ARRAY[num].field_3 = 3
            self.STRUCT_ARRAY[num].field_4 = 4

for num in range(0,100):
    test = STRUCT_1(num)
    print("%i done" % num)
🌐
DaniWeb
daniweb.com › programming › software-development › threads › 351774 › passing-pointer-to-array-of-structures-in-ctypes
python - Passing pointer to array of structures in ... [SOLVED] | DaniWeb
To call pyArrayFiller, I need to generate the python version of the `void *pRecord` which is a pointer to an array of four structures as defined below: ... typedef struct tagMyStruct { double x; double y; double z; double a; double e; double r; double time; USHORT quality; } MyStruct; So I constructed a python version of the same structure: class pyMyStructure(ctypes.Structure): _fields_ = [("x", ctypes.c_double), ("y", ctypes.c_double), ("z", ctypes.c_double), ("a", ctypes.c_double), ("e", ctypes.c_double), ("r", ctypes.c_double), ("time", ctypes.c_double), ("quality", ctypes.c_double)]
🌐
CSDN
devpress.csdn.net › python › 62fee900c677032930804a21.html
python ctypes array of structs - DevPress官方社区
August 19, 2022 - import ctypes from random import randint class STRUCT_2(ctypes.Structure): #_pack_=2 _fields_ = [('field_1', ctypes.c_short), ('field_2', ctypes.c_short), ('field_3', ctypes.c_short), ('field_4', ctypes.c_short), ('field_5', ctypes.c_short), ('field_6', ctypes.c_short), ('field_7', ctypes.c_short), ('field_8', ctypes.c_short)] class STRUCT_1(ctypes.Structure): #_pack_=2 _fields_ = [('elements', ctypes.c_short), #an array of structs ('STRUCT_ARRAY', ctypes.POINTER(STRUCT_2))] def __init__(self,num_of_structs): elems = (STRUCT_2 * num_of_structs)() self.STRUCT_ARRAY = ctypes.cast(elems,ctypes.PO
Top answer
1 of 1
1

I want to start with the note that I don't see where is the entry_count attribute initialized.

Iterating over a pointer as if it was a sized array is conceptually wrong (as also pointed out in [Python 3]: ctypes - A foreign function library for Python). In C, it's possible to go beyond an array bounds, but ctypes forbids it.
Here's a simpler example that uses ctypes.c_char as the base type (MessageEntry correspondent).

code.py:

#!/usr/bin/env python3

import sys
import ctypes


def main():
    CharArr5 = ctypes.c_char * 5
    b5 = b"12345"
    ca5 = CharArr5(*b5)
    print("Print array ...")
    for c in ca5:
        print(c)

    cp = ctypes.cast(ca5, ctypes.POINTER(ctypes.c_char))
    max_values = 10
    print("\nPrint pointer (max {:d} values) ...".format(max_values))
    for idx, c in enumerate(cp):
        print(c)
        if idx >= max_values:
            print("Max value number reached.")
            break


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

Output:

(py_064_03.06.08_test0) e:\Work\Dev\StackOverflow\q054178876>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

Print array ...
b'1'
b'2'
b'3'
b'4'
b'5'

Print pointer (max 10 values) ...
b'1'
b'2'
b'3'
b'4'
b'5'
b'\x00'
b'\x00'
b'\x00'
b'\x00'
b'\x00'
b'\x00'
Max value number reached.

As seen, it is possible to iterate over the pointer, but the loop never ends (it will end when reaching an unavailable / invalid memory address, and the program will segfault (Access Violation)).

Assuming that entry_count is properly initialized (if not, make sure to initialize it), use it to keep the loop inside bounds (as shown below):

for idx in range(msg.entry_count):
    msg.entries[idx]  # Do smth with it
    # ...


for idx, entry in enumerate(msg.entries):
    if idx >= msg.entry_count:
        break
    entry  # Do smth with it
    # ...

Or you could use one of the above to implement Iterator Protocol for Message.

🌐
YouTube
youtube.com › hey delphi
Array : python ctypes array of structs - YouTube
Array : python ctypes array of structsTo Access My Live Chat Page, On Google, Search for "hows tech developer connect"I have a hidden feature that I promised...
Published: June 9, 2023
Views: 11
Top answer
1 of 1
5

There were 2 problems with the code (as I stated in the comment):

  1. print_array_struct.argtype - which is incorrect (and might have disastrous effects). Check (newer) [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for more details

  2. In C the arrays are double based, while in Python they are ctypes.c_int (int) based

For more details, check [Python.Docs]: ctypes - A foreign function library for Python.
I modified your Python code, to correct the above mistakes (and some other minor stuff).

code00.py:

#!/usr/bin/env python

import ctypes as ct
import sys


DLL_NAME = DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")

DOUBLE_10 = ct.c_double * 10


class ArrayStruct(ct.Structure):
    _fields_ = (
        ("first_array", DOUBLE_10),
        ("second_array", DOUBLE_10),
    )


def main(*argv):
    dll_handle = ct.CDLL(DLL_NAME)
    print_array_struct = dll_handle.print_array_struct
    print_array_struct.argtypes = (ArrayStruct,)
    print_array_struct.restype = None

    x1 = DOUBLE_10()
    x2 = DOUBLE_10()
    x1[:] = range(1, 11)
    x2[:] = range(11, 21)
    print([item for item in x1])
    print([item for item in x2])
    arg = ArrayStruct(x1, x2)
    print_array_struct(arg)


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

dll00.c (dummy):

#include <stdio.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif

#define DIM 10


typedef struct {
    double first_array[DIM];
    double second_array[DIM];
} ArrayStruct;


#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API void print_array_struct(ArrayStruct array_struct);

#if defined(__cplusplus)
}
#endif


void print_array_struct(ArrayStruct array_struct)
{
    printf("From C:\n");
    for (int i = 0; i < DIM; ++i) {
        printf("  %2.1lf\n", array_struct.first_array[i]);
    }
}

Output:

(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q050447199]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> ls
code00.py  dll00.c
[064bit prompt]> gcc -fPIC -shared -o dll00.so dll00.c
[064bit prompt]> ls
code00.py  dll00.c  dll00.so
[064bit prompt]>
[064bit prompt]> python3.5 ./code00.py
Python 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609] 064bit on linux

[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
[11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0]
From C:
  1.0
  2.0
  3.0
  4.0
  5.0
  6.0
  7.0
  8.0
  9.0
  10.0

Done.
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
The recommended way to create array types is by multiplying a data type with a positive integer: ... Here is an example of a somewhat artificial data type, a structure containing 4 POINTs among other stuff: >>> from ctypes import * >>> class ...
Find elsewhere
🌐
Chris Heydrick
chrisheydrick.com › 2016 › 02 › 06 › passing-a-ctypes-array-of-struct-from-python-to-dll
Passing a ctypes array of struct from Python to .dll | Chris Heydrick
February 6, 2016 - // #include "stdafx.h" #ifdef STRUCTTEST_EXPORTS #define STRUCTTEST_API __declspec(dllexport) #else #define STRUCTTEST_API __declspec(dllimport) #endif extern "C" { struct structtest { char x; int y; long z; }; STRUCTTEST_API void fillonestruct(struct structtest *t) { t->x = 'A'; t->y = 1234; t->z = 5678; } STRUCTTEST_API void fillmultiplestruct(struct structtest *t, int n) { for (int i = 0; i < n; i++) { t->x = (char)((int)'A' + i); t->y = i; t->z = i; t++; } } } ... from ctypes import * class structtest(Structure): _fields_ = [("x", c_char), ("y", c_int), ("z", c_long)] if __name__ == "__mai
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes_examples.html
More complicated ctypes example - Thematic Tutorials
Next consider the following python helper code. from ctypes import * class double_row_element(Structure): pass double_row_element._fields_=[("value",c_double),("col_index",c_int),("next_element",POINTER(double_row_element) )] class double_sparse_matrix(Structure): _fields_=[("nrows",c_int),("ncols",c_int),("nnz",c_int),("rows",POINTER(POINTER(double_row_element)))] double_sparse_pointer=POINTER(double_sparse_matrix) sparse_library=CDLL("/home/jkantor/linked_list_sparse.so") initialize_matrix=sparse_library.initialize_matrix initialize_matrix.restype=double_sparse_pointer set_value=sparse_library.set_value get_value=sparse_library.get_value get_value.restype=c_double free_matrix=sparse_library.free_matrix
🌐
Stack Overflow
stackoverflow.com › questions › 75739510 › python-ctypes-pointer-to-array-of-structs
Python ctypes pointer to array of structs - Stack Overflow
Instead, create instances of each structure and pass them by address using ctypes.byref(). Here's a working example with a simplfied API since it wasn't provided: test.c · struct NIComplexNumberF32 { float real; float imaginary; }; struct niRFSA_wfmInfo { double absoluteInitialX; double relativeInitialX; double xIncrement; double actualSamples; double offset; double gain; double reserved1; double reserved2; }; __declspec(dllexport) // I'm using Windows, so needs this for function export from DLL void niRFSA_FetchIQSingleRecordComplexF32(struct NIComplexNumberF32* p1, struct niRFSA_wfmInfo* p2) { p1->real = 1.5f; p1->imaginary = 2.75f; p2->absoluteInitialX = 1.5; p2->relativeInitialX = 2.5; p2->xIncrement = 3.5; p2->actualSamples = 4.5; p2->offset = 5.5; p2->gain = 6.5; p2->reserved1 = 0.0; p2->reserved2 = 0.0; }
🌐
Stack Overflow
stackoverflow.com › questions › 53967546 › how-to-build-a-ctypes-array-of-structure-in-python
How to build a ctypes array of structure in python - Stack Overflow
I suggest defining __repr__ in the structure so Node can print itself, and using more "pythonic" looping: from ctypes import * class Node(Structure): _fields_ = [("id", c_uint16), ("version", c_uint8), ("depth", c_uint8)] def __repr__(self): return f'Node(id={self.id}, version={self.version}, depth={self.depth})' dics = [{'ID': '3', 'VERSION': '180', 'DEPTH': '924'}, {'ID': '9', 'VERSION': '180', 'DEPTH': '269'}, {'ID': '2', 'VERSION': '180', 'DEPTH': '537'}] nodeNum = len(dics) NODES = Node * nodeNum nodes = NODES() for node,dic in zip(nodes,dics): node.id = int(dic['ID']) node.version = int(dic['VERSION']) node.depth = int(dic['DEPTH']) for node in nodes: print(node)
Top answer
1 of 1
2

What you're doing is not correct. For the last member of world to be an array of N hellos, it has to be of type hello * N, not hello.

But here, N is 0, so that doesn't really work.

In C, this is called the "struct hack" or "struct array hack". That world struct isn't a real structure that you can construct as-is and pass around. Instead, what you do is malloc up sizeof(world) + N * sizeof(hello) bytes, cast that to world *, and pass that pointer around. Because array indexing doesn't do any type-checking in C, my_world->h[2] will just work (assuming you've allocated enough memory for at least 3 hellos at the end of the world).

But it won't work in Python.

Well, it sort of works, but you have to do more work.


The first option is to not actually define world globally, and instead define a new Structure class every time you want to deal with a world.

For example. let's say we called some function that told us how many hellos there are:

n = lib.number_of_hellos()

And now we want to call a function that gives us the world with that many hellos in it. So:

class world(Structure):
    _pack_ = 1
    _fields_ = [ ("f1", c_ulonglong),
                 ("f2", c_ulonglong),
                 ("f3", c_uint16),
                 ("f4", c_uint16),
                 ("f5", c_uint32),
                 ("f6", hello * n)
              ]

At this point, you can either change the restype of lib.get_world to return a POINTER(world), or you can cast the result that comes back.


Alternatively, you can define world as always having 1 hello, but then, instead of accessing hello directly off a world pointer, you construct a hello * N pointer at the hello's address (or just at the world's address plus the offset of the type's hello; either way is the same).


What if you need to create a world with N hellos in it to pass to C?

You can use the first trick again, constructing a new world class that ends with an array of N hellos, then create an instance of that to pass it to C.

Or you can use the same trick C code uses: keep the global single-hello world class around, and either construct your world objects by creating a buffer and then casting that to a world, or by constructing a world object but then calling resize on it.


There are also workarounds you can do to make life easier. For example, if you're writing the C code yourself (and you can't just not use the struct hack), you can easily add a pair of C functions that compose a no-hello world, an array of hellos, and a count into an N-hello world, and vice-versa.

Or, alternatively, just make a nonhackyworld type that has a pointer to an array of hellos at the end, instead of directly including it, and write C functions to convert back and forth between them.

Then you just ctypes up those functions and call them whenever you want to pass or process a world (or even set them up as value type converters for other functions).

Even if you can't add C functions, you can write those functions in ctypes. They'll be horribly ugly messes of creating buffers and casting pointers, but you only have to write them (and debug the segfaults) once, and then use them everywhere.

🌐
ProgramCreek
programcreek.com › python › example › 1122 › ctypes.Structure
Python Examples of ctypes.Structure
def test_union_packed(self): class Struct(ctypes.Structure): _fields_ = [ ('one', ctypes.c_uint8), ('two', ctypes.c_uint32) ] _pack_ = 1 class Union(ctypes.Union): _pack_ = 1 _fields_ = [ ('a', ctypes.c_uint8), ('b', ctypes.c_uint16), ('c', ctypes.c_uint32), ('d', Struct), ] expected = np.dtype(dict( names=['a', 'b', 'c', 'd'], formats=['u1', np.uint16, np.uint32, [('one', 'u1'), ('two', np.uint32)]], offsets=[0, 0, 0, 0], itemsize=ctypes.sizeof(Union) )) self.check(Union, expected) ... def create_remote_array(subtype, len): class RemoteArray(_ctypes.Array): _length_ = len _type_ = subtype def
🌐
Bytes
bytes.com › home › forum › topic › python
ctypes - pointer to array of structs? - Post.Byes
January 3, 2008 - Using ctypes I reference a structure which contains a pointer to an array of another structure: class SYMBOL(Structur e): _fields_ = [("symbol", c_char_p), ("num", c_int), ("units", c_int), ("baseprice" , c_int), ("active", c_int)] SYMBOL_PTR = POINTER(SYMBOL) class TABLE(Structure ): _fields_ = [("map", SYMBOL_PTR), ("nsymbols", c_uint), ...] Effectively, TABLE.map is an array of TABLE.nsymbols SYMBOLS. How to I reference elements in that array? In C I would just treat TABLE.map like an array and index into it (for i=0; i< TABLE.nsymbols; i++) ...). This is data returned from a C library, not something I'm building in Python to pass into C. Thx, Skip ... Diez B. Roggisch ... (Is this the right place to ask ctypes questions?
🌐
Stack Overflow
stackoverflow.com › questions › 30380551 › accessing-dynamic-array-of-struct-in-python-using-ctypes
c - Accessing dynamic array of struct in Python using ctypes - Stack Overflow
import ctypes class Point(ctypes.Structure): _fields_ = [('x', ctypes.c_int), ('y', ctypes.c_int)] class MyStruct(ctypes.Structure): _fields_ = [('a', ctypes.c_int), ('b', ctypes.c_double), ('p', Point*5), ('dynamic_p', ctypes.POINTER(Point))] simulator = ctypes.cdll.LoadLibrary('your/path/to/test.so') simulator.retIt.restype = ctypes.POINTER(MyStruct) simulator.setIt() pyMS = simulator.retIt() pyMS.contents.dynamic_p[0].x · I can access the p array without a problem.
Top answer
1 of 2
1

you are mixing up between creating a struct, creating an array of struct, and creating a pointer to an array of struct, those are 3 different operations, i have commented the code for clarity.

import numpy as np
from ctypes import cdll
from ctypes import *

class complex(Structure):  # struct with two floats
    _fields_ = (('Re', c_float),
                ('Im', c_float))

    def __init__(self, I_val, Q_val):
        self.Re = I_val
        self.Im = Q_val

    def __repr__(self):
        return f'Complex({self.Re}, {self.Im})'

I = [2,2,3,4]
Q = [5,6,7,8]
lenIQ = len(I)
my_array_type = complex*lenIQ  # create the type of array (complex[])
my_array = my_array_type()  # create the array (complex my_array[4])

for i in range(len(I)):
    my_array[i] = complex(I[i],Q[i])  # fill each individual object in the array

# next line is equivalent to "complex* pointer_to_array = (complex*)(my_array);"
pointer_to_array = cast(my_array, POINTER(complex))  # create the pointer to the first element of the array (complex*)

print(my_array)  # prints type of my_array
print(my_array[0])  # equivalent to c's my_array[0]
print(pointer_to_array[0])  # equivalent to c's pointer_to_array[0]
<__main__.complex_Array_4 object at 0x000002195A9B8C40>
Complex(2.0, 5.0)
Complex(2.0, 5.0)

now for calling

cgmap = cdll.LoadLibrary('./test.dll')
test = cgmap.test
test.argtypes = (c_int, POINTER(complex))
test.restype  = None
test(c_int(lenIQ), pointer_to_array)
2 of 2
1

Make sure your types match. The structure contains c_float not POINTER(c_float) to match the C definition. The .argtypes are c_int and POINTER(Complex).

ctypes can initialize the structure and arrays without a lot of extra work:

test.c

#include <stdio.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct { float Re; float Im; } complex;

API void test(int N, complex *v) {
    int k;
    for(k = 0; k < N; ++k) {
        v[k].Re = v[k].Re + 1;
        v[k].Im = v[k].Im + 1;
        printf("%d %f %f\n", k, v[k].Re, v[k].Im);
    }
}

test.py

import ctypes as ct

class Complex(ct.Structure):
    _fields_ = (('Re', ct.c_float),  # not POINTER(c_float)
                ('Im', ct.c_float))

    def __repr__(self):
        return f'Complex({self.Re:g}{self.Im:+g}j)'

    @staticmethod
    def array(reals, imags):
        '''Helper method to create Complex arrays.
        '''
        return (Complex * len(reals))(*zip(reals, imags))

I = [2,2,3,4]
Q = [5,6,7,8]

arr = Complex.array(I, Q)
print(list(arr)) # easy way to display the contents when __repr__ is defined.

dll = ct.CDLL('./test')
dll.test.argtypes = ct.c_int, ct.POINTER(Complex)
dll.test.restype = None

dll.test(len(arr), arr)

Output:

[Complex(2+5j), Complex(2+6j), Complex(3+7j), Complex(4+8j)]
0 3.000000 6.000000
1 3.000000 7.000000
2 4.000000 8.000000
3 5.000000 9.000000
🌐
GitHub
github.com › python › cpython › issues › 110190
ctypes array cannot be returned by value on ARM64 · Issue #110190 · python/cpython
October 1, 2023 - In Python, I give code that I use to compile (with gcc), and then three different ways to wrap the struct and function in Python using ctypes. I believe the three ways should be equivalent (and they are on Windows and Linux), but not on Darwin (ARM64). ... struct example{double coords[3];}; struct example set_defaults(){ struct example ex; ex.coords[0] = 1.0; ex.coords[1] = 2.0; ex.coords[2] = 3.0; return ex;}
Author: python