Dynamically defining/updating ctypes structure in Python

Question:

I have created subtructure and structure in ctypes as below where I am defining a array of substructure inside structure with some predefined size. (As per requirement SIZE may be set to 0 initially and can varies based on user input).

from ctypes import *

class MySubStructure(Structure):
    _fields_ = [
        ("sub_field1", c_uint32),
        ("sub_field2", c_uint32)
    ]


class MyStructure(Structure):
    SIZE = 2
    _fields_ = [
        ("field1", c_uint32),
        ("field2", c_uint32),
        ("sub_structure_field", ARRAY(SubStructure, SIZE)),
    ]

My goal is to modify this substructure based on user input.

For achieving the same I have tried below options but had no success:

  1. Defining a __init__ method and updating _fields_ while initializing the instance

  2. Updating _fields_ after initializing instance

For both of those options I tried to appending sub_structure_field, updating only size value by accessing through index.

Finally I just want a workaround so that I can use array of structure inside another structure either initializing at runtime or modifying at runtime.

Asked By: Gahan

||

Answers:

Mentioning [Python.Docs]: ctypes – A foreign function library for Python.

The array size must be known at the moment _fields_ is defined.
You could have a factory function which defines the class and returns it.

code00.py:

#!/usr/bin/env python

import ctypes as cts
import sys


class SubStructure(cts.Structure):
    _fields_ = (
        ("sub_field1", cts.c_uint32),
        ("sub_field2", cts.c_uint32),
    )


def structure_factory(size):
    class DynamicStructure(cts.Structure):
        _fields_ = (
            ("field1", cts.c_uint32),
            ("field2", cts.c_uint32),
            ("sub_structure_field", SubStructure * size),  # Equivalent to: cts.ARRAY(SubStructure, size)
        )

    return DynamicStructure


def main(*argv):
    Struct2 = structure_factory(2)  # DynamicStructure type NOT instance
    Struct5 = structure_factory(5)  # -- // --
    print(Struct2.sub_structure_field)
    print(Struct5.sub_structure_field)


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

Output:

[cfati@CFATI-5510-0:e:WorkDevStackOverflowq057417435]> "e:WorkDevVEnvspy_064_03.07.03_test0Scriptspython.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 064bit on win32

<Field type=SubStructure_Array_2, ofs=8, size=16>
<Field type=SubStructure_Array_5, ofs=8, size=40>

Done.

You could also take a look at [SO]: Setting _fields_ dynamically in ctypes.Structure (@CristiFati’s answer).

Answered By: CristiFati