'How to define C-Enumeration types in python
I have an enumeration data type in C. How should I declare that in python-ctypes? I want this enum variable to be part of a structure and the assignment of the values to this structure would be done through memmove. After assigning, I want to display the values of each variables in the structure, and for the enum types I want to display the enum-string.
Solution 1:[1]
Antti Haapala did a fantastic job answering! I, however, did run into some minor issues when using it with Python 3.2.2 that I believe are worth noting. Instead of:
class CEnumeration(c_uint):
__metaclass__ = EnumerationType
_members_ = {}
You need to do:
class CEnumeration(c_uint, metaclass = EnumerationType):
_members_ = {}
Also, int and long have been unified in Python 3 so:
def __eq__(self, other):
if isinstance(other, (int, long)):
return self.value == other
return type(self) == type(other) and self.value == other.value
Becomes:
def __eq__(self, other):
if isinstance(other, int):
return self.value == other
return type(self) == type(other) and self.value == other.value
Solution 2:[2]
Here is an extension of the solution from Antti Happala, using the modifications for Python 3 as suggested by Tigger, plus an exentension for arbitrary ctypes as base class (e.g. uint8 vs. uint16):
from ctypes import *
def TypedEnumerationType(tp):
class EnumerationType(type(tp)): # type: ignore
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_
else:
_members_ = dict["_members_"]
dict["_reverse_map_"] = {v: k for k, v in _members_.items()}
cls = type(tp).__new__(metacls, name, bases, dict)
for key, value in cls._members_.items():
globals()[key] = value
return cls
def __repr__(self):
return "<Enumeration %s>" % self.__name__
return EnumerationType
def TypedCEnumeration(tp):
class CEnumeration(tp, metaclass=TypedEnumerationType(tp)):
_members_ = {}
def __repr__(self):
value = self.value
return f"<{self.__class__.__name__}.{self._reverse_map_.get(value, '(unknown)')}: {value}>"
def __eq__(self, other):
if isinstance(other, int):
return self.value == other
return type(self) == type(other) and self.value == other.value
return CEnumeration
Here is a small unit test for this, showing that it actually works to differentiate between unit8 and uint16 enums:
class Foo(TypedCEnumeration(c_uint16)):
A = 42
B = 1337
class Bar(TypedCEnumeration(c_uint8)):
A = 5
B = 23
assert isinstance(Foo(Foo.A), c_uint16)
assert isinstance(Bar(Bar.A), c_uint8)
assert type(Foo.A) == int
assert Foo.A == 42
assert str(Foo(Foo.A)) == "<Foo.A: 42>"
assert str(Bar(Bar.B)) == "<Bar.B: 23>"
class FooBar(Structure):
_pack_ = 1
_fields_ = [("foo", Foo), ("bar", Bar)]
foobar = FooBar(Foo.A, Bar.B)
assert sizeof(foobar) == 3
assert foobar.foo.value == 42
assert foobar.bar.value == 23
assert [int(x) for x in bytes(foobar)] == [42, 0, 23]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Tigger |
Solution 2 | Patrick Roocks |