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
Passing pointer to array of structures in ctypes - python
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)]
Discussions

Python ctypes pointer to array of structs - Stack Overflow
Ive got a similar question to ctypes and array of structs but im not looking to return the structure. Rather, my structure is passed into the function call as a pointer and then i want the values p... More on stackoverflow.com
🌐 stackoverflow.com
python - ctypes struct containing arrays - Stack Overflow
Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... I'm trying to use ctypes. I'm interested in manipulating C structs containing arrays. More on stackoverflow.com
🌐 stackoverflow.com
May 21, 2018
Python ctype struct with array of structs - Stack Overflow
I have some struct, with dynamic entries count. I receiving bytearray from UDP and parsing this message as follows: class MsgStruct(Structure): _pack_ = 1 def __init__(self, data=None): ... More on stackoverflow.com
🌐 stackoverflow.com
python - ctypes and array of structs - Stack Overflow
I'd like to read an array of structs defined in a C library using ctypes and python. The C struct is simply struct particle { double x; double y; } I have a function that returns a pointer t... More on stackoverflow.com
🌐 stackoverflow.com
🌐
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
🌐
CSDN
devpress.csdn.net › python › 62fee900c677032930804a21.html
python ctypes array of structs - DevPress官方社区
August 19, 2022 - 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', ...
🌐
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 ...
🌐
Stack Overflow
stackoverflow.com › questions › 75739510 › python-ctypes-pointer-to-array-of-structs
Python ctypes pointer to array of structs - Stack Overflow
#create instances data = ctypes.POINTER(NIComplexNumberF32_struct_data)() wfmInfo = ctypes.POINTER(niRFSA_wfmInfo_struct_data)() ... It only creates NULL pointers and causes the RuntimeError you see. Instead, create instances of each structure and pass them by address using ctypes.byref().
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.
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.

Find elsewhere
🌐
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.
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes_examples.html
More complicated ctypes example - Thematic Tutorials
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
🌐
Reddit
reddit.com › r/autohotkey › ctypes create arrays in struct
r/AutoHotkey on Reddit: ctypes create arrays in struct
May 17, 2024 -

I am using this script in my AHK code but while attempting to create a list in a struct, I got an error. I don't know if there is a specific prefix to to the variable I need to use so I am just using the prefix that would go for the values in the array. This is my code:

math := ctypes.struct("float cosvar; float sinvar;")({
    cosvar: [0.0],
    sinvar: [0.0]
})

How should I fix this?

🌐
Stack Overflow
stackoverflow.com › questions › 45267013 › ctypes-array-of-struct-in-struct-how-can-i-access-indexes-other-than-0
python - Ctypes array of struct in struct. How can I access indexes other than 0? - Stack Overflow
from ctypes import * MAX_LENGTH_UUID = 256 MAX_DEVICES_PER_NODE = 16 class uuid_t(Structure): pass uuid_t._fields_ = [("uuid", c_char*MAX_LENGTH_UUID)] class device_list_t(Structure): pass device_list_t._fields_ = [("num_devices", c_uint), ("dev_list", uuid_t*MAX_DEVICES_PER_NODE)] def main(): folder = os.path.dirname(os.path.abspath(__file__)) dll_path = os.path.join(folder, "libnmos.so") dll = cdll.LoadLibrary(dll_path) print '' dev_list = device_list_t() print '[GetDeviceList] Returned value: ' + str(dll.is04_get_device_lists(byref(dev_list))) print '[GetDeviceList] Total devices: ' + str(d
Top answer
1 of 1
2

Here's a way but it isn't pretty. ctypes doesn't do variable arrays in a structure so to access the variable data requires some casting.

test.c Implements a test function returning the variable structure data. In this case I hard-coded a return array of size 4 but it could be any size.

#include <stdlib.h>

typedef struct STATE {
    int a;
    int b;
} STATE;

typedef struct DXYZ {
    int count;
    STATE state[];
} DXYZ, *PDXYZ;

__declspec(dllexport) PDXYZ get(void)
{
    PDXYZ pDxyz = malloc(sizeof(DXYZ) + sizeof(STATE) * 4);
    pDxyz->count = 4;
    pDxyz->state[0].a = 1;
    pDxyz->state[0].b = 2;
    pDxyz->state[1].a = 3;
    pDxyz->state[1].b = 4;
    pDxyz->state[2].a = 5;
    pDxyz->state[2].b = 6;
    pDxyz->state[3].a = 7;
    pDxyz->state[3].b = 8;
    return pDxyz;
}

__declspec(dllexport) void myfree(PDXYZ pDxyz)
{
    free(pDxyz);
}

test.py

from ctypes import *
import struct

class State(Structure):
    _fields_ = [('a',c_int),
                ('b',c_int)]

class DXYZ(Structure):
    _fields_ = [('count',c_int),      # Number of state objects
                ('state',State * 0)]  # Zero-sized array

# Set the correct arguments and return type for the DLL functions.
dll = CDLL('test')
dll.get.argtypes = None
dll.get.restype = POINTER(DXYZ)
dll.myfree.argtypes = POINTER(DXYZ),
dll.myfree.restype = None

pd = dll.get()    # Get the returned pointer
d = pd.contents   # Dereference it.

print('count =',d.count)
# Cast a pointer to the zero-sized array to the correct size and dereference it.
s = cast(byref(d.state),POINTER(State * d.count)).contents

for c in s:
    print(c.a,c.b)

dll.myfree(pd)

Output:

count = 4
1 2
3 4
5 6
7 8
🌐
ProgramCreek
programcreek.com › python › example › 1122 › ctypes.Structure
Python Examples of ctypes.Structure
def compile(self, structure): source = self.gen_struct_class(structure) c = compile(source, '<compiled>', 'exec') env = { 'OrderedDict': OrderedDict, 'Structure': Structure, 'Instance': Instance, 'Expression': Expression, 'EnumInstance': EnumInstance, 'PointerInstance': PointerInstance, 'BytesInteger': BytesInteger, 'BitBuffer': BitBuffer, 'struct': struct, 'xrange': xrange, } exec(c, env) sc = env[structure.name](self.cstruct, structure, source) return sc ... def as_ctypes_type(dtype): r""" Convert a dtype into a ctypes type. Parameters ---------- dtype : dtype The dtype to convert Returns ------- ctype A ctype scalar, union, array, or struct Raises ------ NotImplementedError If the conversion is not possible Notes ----- This function does not losslessly round-trip in either direction.
🌐
Stack Overflow
stackoverflow.com › questions › 29929920 › how-to-pass-a-list-array-of-structs-from-python-to-c
How to pass a list/array of structs from python to C - Stack Overflow
April 28, 2015 - I solved my problem by: 1. Declaring an array of pointers to my struct: nodeArrayType = ctypes.POINTER(STATION_MM_NODE) * 1024 nodeArray = nodeArrayType() nstn = 0
Top answer
1 of 1
8

The error explains exactly the problem, but in a slightly convoluted manner:

The correct way to initialize such an array is as follows:

structs = [A(1, 2), A(1, 2)]
array = (A * 2)(*structs)

Or, if you can define them all during the array constructor, as follows:

array = (A * 2)(A(1, 2), A(1, 2))

Now, why does this work and your solution not? Since I am passing an instance of A for each argument to the constructor of A * 2, which is an array of A. The array requires an instance of A for each argument passed, which is precisely the error message. It does not expect to convert arguments into A, it expects an instance of A itself.

The step-by-step way is as follows:

from ctypes import *
class A(Structure):
    _fields_ = [('a', c_int), ('b', c_int)]

# this create a class which expects 2 A instances
a_array_factory = A * 2
# our first A instance
struct_1 = A(1, 2)
# our second A instance
struct_2 = A(2, 3)
# initialize our array
array = a_array_factory(struct_1, struct_2)

EDIT:

Since a structure does not have a native Python type, there is no solution, to my knowledge, without creating intermediary structs. According to the Python documentation, the only native types with C primitives are the following:

ctypes type     C type                                      Python type
c_bool          _Bool                                       bool (1)
c_char          char                                        1-character string
c_wchar         wchar_t                                     1-character unicode string
c_byte          char                                        int/long
c_ubyte         unsigned char                               int/long
c_short         short                                       int/long
c_ushort        unsigned short                              int/long
c_int           int                                         int/long
c_uint          unsigned int                                int/long
c_long          long                                        int/long
c_ulong         unsigned long                               int/long
c_longlong      __int64 or long long                        int/long
c_ulonglong     unsigned __int64 or unsigned long long      int/long
c_float         float                                       float
c_double        double                                      float
c_longdouble    long double                                 float
c_char_p        char * (NUL terminated)                     string or None
c_wchar_p       wchar_t * (NUL terminated)                  unicode or None
c_void_p        void *                                      int/long or Nonee or None
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.