I really hope some Python/Ctypes/C expert can help me with this one, it is probably my lack of knowledge on the typing structure to correctly use Ctypes when using Python to interact with C libraries.
Objective: I need to access few library functions loading a DLL using ctypes and interacting with it. The idea works fine most of the time, but there are few functions that take enums as parameters and those enums are very sensitive when it comes to int type. Here is a fake example:
typedef enum led_property : uint8_t {
LED_OFF = 0
LED_POWER
}
int32_t configure_led(const led_property, const int32_t value)
This is the kind of enum the functions receive, not only for uint8_t, but also for int32_t, int_64t and so on.
From a python recipe I found online, I managed to "adapt" python enums with ctypes types:
class EnumerationTypeUInt8(type(c_uint8)):
def __new__(metacls, name, bases, dict):
if not "_members_" in dict:
_members_ = {}
for key, value in dict.items():
if not key.startswith("_"):
_members_[key] = value
dict["_members_"] = _members_
cls = type(c_uint8).__new__(metacls, name, bases, dict)
for key, value in cls._members_.items():
globals()[key] = value
return cls
def __contains__(self, value):
return value in self._members_.values()
def __repr__(self):
return "<Enumeration {}>".format(self.__name__)
def EnumerationUInt8(c_uint8):
__metaclass__ = EnumerationTypeUInt8
_members_ = {}
def __init__(self, value):
for k, v in self._members_.items():
if v == value:
self.name = k
break
else:
raise ValueError("No enumeration member with value {}".format(value))
c_uint8.__init__(self, value)
@classmethod
def from_param(cls, param):
if isinstance(param, EnumerationUInt8):
if param.__class__ != cls:
raise ValueError("Can not mix enumeration members")
else:
return param
else:
return cls(param)
def __repr__(self):
return "<member {}={} of {}".format(self.name, self.value, self.__class__)
I'm loading the library and decorating its functions as follows:
class LedProperty(EnumerationUInt8):
LED_OFF = c_uint8(0)
LED_POWER = c_uint8(1)
lib = "library.dll"
self._lib = CDLL(lib)
configure_led = self._lib.configure_led
configure_led.argtypes = [LedProperty, c_int32]
configre_led.restype = c_int32
The problem is that I tried all that I could and I can never call that configure_led python function properly, most of the time I get the following error:
ctypes.ArgumentError class 'ValueError' No enumeration member with value c_ubyte(1)
or
ctypes.ArgumentError class 'ValueError' No enumeration member with value 1
That is happening because I can see when debugging that the "EnumerationUInt8" "self.members.items()" is always an empty dict. So probably this custom enums with ctypes are not loading their members properly. I'm always ending on that "else: return cls(param)"
Tried:
configure_led(LedProperty.LED_POWER, 5)
configure_led(LedProperty.LED_POWER.value, 5)
configure_led(c_uint8(LedProperty.LED_POWER), 5)
... and so on! Nothing seems to be correct.
Does anyone knows how to properly declare Enums using cytpes types and later use those enums as arguments for functions?
Obs.: I'm currently using Python 3.8.3 Thanks in advance!