Probably something like this:

def getdict(struct):
    return dict((field, getattr(struct, field)) for field, _ in struct._fields_)

>>> x = S1()
>>> getdict(x)
{'A': <__main__.c_ushort_Array_10 object at 0x100490680>, 'C': 0L, 'B': 0L}

As you can see, it works with numbers but it doesn't work as nicely with arrays -- you will have to take care of converting arrays to lists yourself. A more sophisticated version that tries to convert arrays is as follows:

def getdict(struct):
    result = {}
    for field, _ in struct._fields_:
         value = getattr(struct, field)
         # if the type is not a primitive and it evaluates to False ...
         if (type(value) not in [int, long, float, bool]) and not bool(value):
             # it's a null pointer
             value = None
         elif hasattr(value, "_length_") and hasattr(value, "_type_"):
             # Probably an array
             value = list(value)
         elif hasattr(value, "_fields_"):
             # Probably another struct
             value = getdict(value)
         result[field] = value
    return result

If you have numpy and want to be able to handle multidimensional C arrays, you should add import numpy as np and change:

 value = list(value)

to:

 value = np.ctypeslib.as_array(value).tolist()

This will give you a nested list.

Answer from Tamás on Stack Overflow
🌐
GitHub
github.com › osvlabs › struct-json
GitHub - osvlabs/struct-json: FOR converting data between BINARY STRUCT and JSON. It always be used for parse data between web application and socket protocols. Such as convert between MQTT and modbus(TCP or 485) · GitHub
$ g++ -fPIC -shared json2struct.cc -std=c++14 -o libs/json2struct.so $ g++ -fPIC -shared struct2json.cc -std=c++14 -o libs/struct2json.so · You can load those librarries in any languages such as Python, PHP, Java or any other language which support stardard "C" library. Below are a simple in python · import ctypes struct2json = ctypes.CDLL('./libs/struct2json.so') struct2json.test.restype = ctypes.c_char_p struct2json.parse.restype = ctypes.c_char_p print(struct2json.test(b'\x41\x42\x43\x44\x45\x46\x47')) # print(struct2json.test(b"abc")) print(struct2json.parse(b'\x12\x27\x00\x00\x68\xAB\x4A\x5E\x00\x00\x00\x00\x01\x41\x42\x43\x44\x45\x46\x00\x0A\x00\x00\x00')) There are 3 samples in folder /python ·
Author: osvlabs
🌐
CodersLegacy
coderslegacy.com › home › structs with python ctypes
Structs with Python Ctypes - CodersLegacy
October 30, 2022 - It needs to be created with a special attribute called _fields_ which ctypes uses to extract information about the attributes inside the “struct”. The _fields_ attribute contains 2-value tuple pairs. The first value represents the name of the attribute, and the second represents the datatype. Remember, we need to be using ctypes datatypes here, not regular Python datatypes.
🌐
Python
python-list.python.narkive.com › 2sDZ0w1j › ctypes-structure-serialization
ctypes Structure serialization
Permalink I'm not quite familiar with python serialization but the picle module, at least, doesn't seem to be able to serialize a ctypes Structure with array-fields. Even if it was, the ASCII file produced is not in a human-friendly format. Could someone please suggest a method of saving and loading the fields in ctypes' Structure derived class to a json or better yet, to something like INFO http://www.boost.org/doc/libs/1_41_0/doc/html/boost_propertytree/parsers.html#boost_propertytree.parsers.info_parser For example, I have an object of ...
🌐
Iotexpert
iotexpert.com › stupid-python-tricks-c-structures-using-the-ctypes-module-part-2
Stupid Python Tricks: C-Structures using the ctypes Module (part 2) – IoT Expert
class MyStruct1(ctypes.BigEndianStructure): _pack_ = 1 _fields_ = [ ("first",ctypes.c_uint8,4), ("second",ctypes.c_uint8,4), ("third",ctypes.c_uint8,8), ] def __new__(self,sb=None): if(sb): return self.from_buffer_copy(sb) else: return ctypes.BigEndianStructure.__new__(self) def __init__(self,sb=None): pass print("Next case") c = MyStruct1() c.first = 0xa c.second = 0xb c.third = 0xcd print(bytes(c)) d = MyStruct1(b'\xab\xcd') print(bytes(d)) Now when I run it, things are good. (venv) $ python ex-struct.py Next case b'\xab\xcd' b'\xab\xcd'
Top answer
1 of 4
13

Probably something like this:

def getdict(struct):
    return dict((field, getattr(struct, field)) for field, _ in struct._fields_)

>>> x = S1()
>>> getdict(x)
{'A': <__main__.c_ushort_Array_10 object at 0x100490680>, 'C': 0L, 'B': 0L}

As you can see, it works with numbers but it doesn't work as nicely with arrays -- you will have to take care of converting arrays to lists yourself. A more sophisticated version that tries to convert arrays is as follows:

def getdict(struct):
    result = {}
    for field, _ in struct._fields_:
         value = getattr(struct, field)
         # if the type is not a primitive and it evaluates to False ...
         if (type(value) not in [int, long, float, bool]) and not bool(value):
             # it's a null pointer
             value = None
         elif hasattr(value, "_length_") and hasattr(value, "_type_"):
             # Probably an array
             value = list(value)
         elif hasattr(value, "_fields_"):
             # Probably another struct
             value = getdict(value)
         result[field] = value
    return result

If you have numpy and want to be able to handle multidimensional C arrays, you should add import numpy as np and change:

 value = list(value)

to:

 value = np.ctypeslib.as_array(value).tolist()

This will give you a nested list.

2 of 4
3

A little bit more general purpose to handle double arrays, and arrays of structures, and bitfields.

def getdict(struct):
    result = {}
    #print struct
    def get_value(value):
         if (type(value) not in [int, float, bool]) and not bool(value):
             # it's a null pointer
             value = None
         elif hasattr(value, "_length_") and hasattr(value, "_type_"):
             # Probably an array
             #print value
             value = get_array(value)
         elif hasattr(value, "_fields_"):
             # Probably another struct
             value = getdict(value)
         return value
    def get_array(array):
        ar = []
        for value in array:
            value = get_value(value)
            ar.append(value)
        return ar
    for f  in struct._fields_:
         field = f[0]
         value = getattr(struct, field)
         # if the type is not a primitive and it evaluates to False ...
         value = get_value(value)
         result[field] = value
    return result
Top answer
1 of 1
1

Hopefully this helps someone else. After debugging a bit more, I ended up in the file corresponding to the error(encoder.py) and found that I was calling JSONEncoder.default in cases where I didn't handle the instance. In this case, I was assuming c_uint32 was an instance of int:

class ReportEncoder(JSONEncoder):

    def default(self, obj):

        if isinstance(obj, (Array, list)):
            return [self.default(e) for e in obj]

        if isinstance(obj, _Pointer):
            return self.default(obj.contents) if obj else None

        if isinstance(obj, _SimpleCData):
            return self.default(obj.value)

        if isinstance(obj, (bool, int, float, str)):
            return obj

        if obj is None:
            return obj

        if isinstance(obj, (Structure, Union)):
            result = {}
            anonymous = getattr(obj, '_anonymous_', [])

            for key, _ in getattr(obj, '_fields_', []):
                value = getattr(obj, key)

                # private fields don't encode
                if key.startswith('_'):
                    continue

                if key in anonymous:
                    result.update(self.default(value))
                else:
                    result[key] = self.default(value)

            return result

        return JSONEncoder.default(self, obj)

But apparently c_uint32 in an instance of long, not int. I added long to the instance list:

if isinstance(obj, (bool, int, float, long, str)):
    return obj

And everything worked from there. The full JSONEncoder class can be found below:

class ReportEncoder(JSONEncoder):

    def default(self, obj):

        if isinstance(obj, (Array, list)):
            return [self.default(e) for e in obj]

        if isinstance(obj, _Pointer):
            return self.default(obj.contents) if obj else None

        if isinstance(obj, _SimpleCData):
            return self.default(obj.value)

        if isinstance(obj, (bool, int, float, long, str)):
            return obj

        if obj is None:
            return obj

        if isinstance(obj, (Structure, Union)):
            result = {}
            anonymous = getattr(obj, '_anonymous_', [])

            for key, _ in getattr(obj, '_fields_', []):
                value = getattr(obj, key)

                # private fields don't encode
                if key.startswith('_'):
                    continue

                if key in anonymous:
                    result.update(self.default(value))
                else:
                    result[key] = self.default(value)

            return result

        return JSONEncoder.default(self, obj)
🌐
ProgramCreek
programcreek.com › python › example › 1122 › ctypes.Structure
Python Examples of ctypes.Structure
def test_union_with_struct_packed(self): class Struct(ctypes.Structure): _pack_ = 1 _fields_ = [ ('one', ctypes.c_uint8), ('two', ctypes.c_uint32) ] class Union(ctypes.Union): _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 test_union_packed(self): class Struct(ctypes.Structure): _fields_ = [ ('one', ctypes.c_uint8), ('two',
Find elsewhere
🌐
Python
docs.python.org › 3 › library › struct.html
struct — Interpret bytes as packed binary data
JSON encoder and decoder. ... Python object serialization. Two main applications for the struct module exist, data interchange between Python and C code within an application or another application compiled using the same compiler (native formats), and data interchange between applications using agreed upon data layout (standard formats).
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
Here is an example of a somewhat artificial data type, a structure containing 4 POINTs among other stuff: >>> from ctypes import * >>> class POINT(Structure): ... _fields_ = ("x", c_int), ("y", c_int) ... >>> class MyStruct(Structure): ... _fields_ = [("a", c_int), ...
🌐
Medium
medium.com › @datasciencefilmmaker › my-god-its-full-of-stars-2-d75d01d2cb94
My God, It’s Full of Stars (2/7) — Accessing C Structures in Python Using Ctypes | by Data Science Filmmaker | Medium
January 18, 2024 - It makes extensive use of structures and pointers to make accessing memory more efficient than the equivalent object-oriented version, and way more efficient than the Python equivalent would be. I wanted to take advantage of the speed of C while still using mostly Python syntax. Enter the “ctypes” library.
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes_examples.html
More complicated ctypes example - Thematic Tutorials
Let’s discuss the above code. The original C code stored a sparse matrix as a linked list. The python code uses the ctypes Structure class to create structures mirroring the structs in the C code. To create python object representing a C struct, simply create class that derives from Structure.
Top answer
1 of 1
1

With ctypes, the structure can be declared and used directly. If you receive the data as a buffer of 32-bit values, you can cast that buffer into the structure as shown below:

import ctypes as ct

MAX_AXIS = 3  # Not provided by OP, guess...

class FAXIS(ct.Structure):
    _fields_ = (('absolute', ct.c_long * MAX_AXIS),
                ('machine', ct.c_long * MAX_AXIS),
                ('relative', ct.c_long * MAX_AXIS),
                ('distance', ct.c_long * MAX_AXIS))

    def __repr__(self):
        return f'FAXIS({list(self.absolute)}, {list(self.machine)}, {list(self.relative)}, {list(self.distance)})'

class OAXIS(ct.Structure):
    _fields_ = (('absolute', ct.c_long),
                ('machine', ct.c_long),
                ('relative', ct.c_long),
                ('distance', ct.c_long))

    def __repr__(self):
        return f'OAXIS({self.absolute}, {self.machine}, {self.relative}, {self.distance})'

class POS(ct.Union):
    _fields_ = (('faxis', FAXIS),
                ('oaxis', OAXIS))

    def __repr__(self):
        return f'POS({self.faxis!r}, {self.oaxis!r})'

class ODBDY2(ct.Structure):
    _fields_ = (('dummy', ct.c_short),
                ('axis', ct.c_short),
                ('alarm', ct.c_long),
                ('prgnum', ct.c_long),
                ('prgmnum', ct.c_long),
                ('seqnum', ct.c_long),
                ('actf', ct.c_long),
                ('acts', ct.c_long),
                ('pos', POS))

    def __repr__(self):
        return f'ODBDY2({self.dummy}, {self.axis}, {self.alarm}, {self.prgnum}, {self.prgmnum}, {self.seqnum}, {self.actf}, {self.acts}, {self.pos!r})'

cnc_one_axis = (ct.c_uint32 * 11)(1,2,3,4,5,6,7,8,9,10,11)
cnc_all_axis = (ct.c_uint32 * 19)(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)

p = ct.pointer(cnc_all_axis)
data = ct.cast(p, ct.POINTER(ODBDY2))
print(data.contents)

p = ct.pointer(cnc_one_axis)
data = ct.cast(p, ct.POINTER(ODBDY2)) # Notice only OAXIS has valid data
print(data.contents)

Output:

ODBDY2(1, 0, 2, 3, 4, 5, 6, 7, POS(FAXIS([8, 9, 10], [11, 12, 13], [14, 15, 16], [17, 18, 19]), OAXIS(8, 9, 10, 11)))
ODBDY2(1, 0, 2, 3, 4, 5, 6, 7, POS(FAXIS([8, 9, 10], [11, 0, 1], [0, -1838440016, 32762], [1, 0, -1]), OAXIS(8, 9, 10, 11)))
Top answer
1 of 2
15

Just create a pointer, assign the data afterwards;

import ctypes

class EmxArray(ctypes.Structure):
    """ creates a struct to match emxArray_real_T """

    _fields_ = [('data', ctypes.POINTER(ctypes.c_double)),
                ('size', ctypes.POINTER(ctypes.c_int)),
                ('allocatedSize', ctypes.c_int),
                ('numDimensions', ctypes.c_int),
                ('canFreeData', ctypes.c_bool)]

data = (1.3, 3.5, 2.7, 4.1)
L = len(data)

e = EmxArray()
e.data = (ctypes.c_double * L)(*data)
e.size = (ctypes.c_int * 1)(L)
# et cetera
2 of 2
4

I'm not well-versed with the Python-C interface, so what I'm suggesting may be less than ideal. My guess is that likely the crash is because x->data is never initialized and the memory to which it is pointing isn't allocated.

An approach I have taken when interfacing to MATLAB Coder generated code from other languages in the presence of emxArray arguments is to hand-write a C interface function that provides a simpler API. This relieves the burden of needing to construct an emxArray in the other environment (Android Java in my particular case). If the generated function foo takes and returns a 2-D double array, then something like the following could work:

void foo(double *x, int *szx, double **y, int *szy);

This function would take a pointer to the input data and its size and provide a pointer to the output data and its size. The implementation would look something like:

void foo(double *x, int *szx, double **y, int *szy) 
{
  emxArray_real_T *pEmx;
  emxArray_real_T *pEmy;

  /* Create input emxArray assuming 2-dimensional input */
  pEmx = emxCreateWrapper_real_T(x, szx[0], szx[1]);

  /* Create output emxArray (assumes that the output is not */
  /* written before allocation occurs) assuming 2-D output  */
  pEmy = emxCreateWrapper_real_T(NULL, 0, 0);

  /* Call generated code (call foobar_initialize/terminate elsewhere) */
  foobar(pEmx, pEmy);

  /* Unpack result - You may want to MALLOC storage in *y and */
  /* MEMCPY there alternatively                               */
  *y = pEmy->data;
  szy[0] = pEmy->size[0];
  szy[1] = pEmy->size[1];

  /* Clean up any memory allocated in the emxArrays (e.g. the size vectors) */
  emxDestroyArray_real_T(pEmx);
  emxDestroyArray_real_T(pEmy);
}

You should be able to call this function from Python more simply and pass in the desired data as needed.

My other answer has more details on the emxArray_* functions found in the file foobar_emxAPI.h.

🌐
Stanford
sporadic.stanford.edu › thematic_tutorials › numerical_sage › ctypes_examples.html
More complicated ctypes example — Thematic Tutorials v9.3.beta9
Let’s discuss the above code. The original C code stored a sparse matrix as a linked list. The python code uses the ctypes Structure class to create structures mirroring the structs in the C code. To create python object representing a C struct, simply create class that derives from Structure.
🌐
University of North Carolina
cs.unc.edu › ~gb › blog › 2007 › 02 › 11 › ctypes-tricks
ctypes tricks
February 11, 2007 - Structures that can print themselves and magically accept tuples as arguments · # hack the ctypes.Structure class to include printing the fields class _Structure(Structure): def __repr__(self): '''Print the fields''' res = [] for field in self._fields_: res.append('%s=%s' % (field[0], repr(getattr(self, field[0])))) return self.__class__.__name__ + '(' + ','.join(res) + ')' @classmethod def from_param(cls, obj): '''Magically construct from a tuple''' if isinstance(obj, cls): return obj if isinstance(obj, tuple): return cls(*obj) raise TypeError