Muhammed B. Aydemir
Muhammed B. Aydemir

Reputation: 1015

ctypes Structure strange behaviour?

I'm defining my structure in python according to the docs (or so I think) but when calling the c function the values don't match:

Python code:

print(f"SchemaID:{appHandResp.supportedAppProtocolRes.SchemaID}, SchemaID_isUsed:{appHandResp.supportedAppProtocolRes.SchemaID_isUsed}")
errn = openv2g.encode_appHandExiDocument(byref(oStream), byref(appHandResp))

C code:

int encode_appHandExiDocument(bitstream_t* stream, struct appHandEXIDocument* exiDoc) {
    if (exiDoc->supportedAppProtocolRes_isUsed)
        printf("SchemaID:%d, SchemaID_isUsed:%d\n", exiDoc->supportedAppProtocolRes.SchemaID, exiDoc->supportedAppProtocolRes.SchemaID_isUsed);

Output:

# in Python right before calling the C function
SchemaID:2, SchemaID_isUsed:1
# in the C function
SchemaID:1, SchemaID_isUsed:0

The structure definitons:

# Python3.8

appHandresponseCodeType = ctypes.c_uint8
appHandresponseCodeType_OK_SuccessfulNegotiation,\
appHandresponseCodeType_OK_SuccessfulNegotiationWithMinorDeviation,\
appHandresponseCodeType_Failed_NoNegotiation = map(appHandresponseCodeType, range(3))

class appHandAnonType_supportedAppProtocolRes(ctypes.Structure):
    _fields_ = [
        ("ResponseCode", appHandresponseCodeType),
        ("SchemaID", ctypes.c_uint8),
        ("SchemaID_isUsed", ctypes.c_uint, 1),
    ]

.

// C

typedef enum {
    appHandresponseCodeType_OK_SuccessfulNegotiation = 0,
    appHandresponseCodeType_OK_SuccessfulNegotiationWithMinorDeviation = 1,
    appHandresponseCodeType_Failed_NoNegotiation = 2
} appHandresponseCodeType;

struct appHandAnonType_supportedAppProtocolRes {
    appHandresponseCodeType ResponseCode ;
    uint8_t SchemaID ;
    unsigned int SchemaID_isUsed:1;
};

Playing with the structure definition in python I was able to get the expected result:

appHandresponseCodeType = ctypes.c_uint
appHandresponseCodeType_OK_SuccessfulNegotiation,\
appHandresponseCodeType_OK_SuccessfulNegotiationWithMinorDeviation,\
appHandresponseCodeType_Failed_NoNegotiation = map(appHandresponseCodeType, range(3))


class appHandAnonType_supportedAppProtocolRes(ctypes.Structure):
    _fields_ = [
        ("ResponseCode", appHandresponseCodeType),
        ("SchemaID", ctypes.c_uint8),
        ("SchemaID_isUsed", ctypes.c_uint8),
    ]

Output:

# in Python right before calling the C function
SchemaID:2, SchemaID_isUsed:1
# in the C function
SchemaID:2, SchemaID_isUsed:1

I can't explain why this is the case, am I defining my structure in ctypes wrong?

Upvotes: 0

Views: 112

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 177471

This should work for you. A C enum is equivalent to a c_int. You don't need to cast the enum values. Just assign them to ResponseCode as needed. It is odd to use a bitfield for a single bit. That one bit will still occupy a full c_uint (typically 32-bits) anyway. There will also normally be three padding bytes between the c_uint8 and the next c_uint for alignment purposes, unless you also set _pack_ as well (must be before _fields_ if used). Check the C header for any packing directives.

from ctypes import *

appHandresponseCodeType_OK_SuccessfulNegotiation = 0
appHandresponseCodeType_OK_SuccessfulNegotiationWithMinorDeviation = 1
appHandresponseCodeType_Failed_NoNegotiation = 2

class appHandAnonType_supportedAppProtocolRes(Structure):
    # _pack_ = 1   # if needed, uncomment.
    _fields_ = (('ResponseCode',c_int),
                ('SchemaID',c_uint8),
                ('SchemaID_isUsed',c_uint,1))

Upvotes: 1

mevets
mevets

Reputation: 10435

Not 100%, but I think that this shows the underlying problem:

#include <stdio.h>
  
typedef struct A {
        char x;
        unsigned y:1;
} A;


int main() {
        printf("%zu\n", sizeof(A));
        return 0;
}

prints the value '4' using clang/macos/64bit, gcc/linux/64bit. It is because of the bitfield permitting the compiler to compact the structure.

Upvotes: 1

Related Questions