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.
-
1What class, exactly, are you referring to? – Makoto Feb 09 '15 at 15:51
-
Yes, it is. https://docs.python.org/2/library/ctypes.html Last time I checket, addressof was not C ;) – deets Feb 09 '15 at 15:51
-
"header" is an instance of Ctypes-Structure. I want to know which method of the class that "header" is an instance of, will be called when I try to assign the variables of the "header"-instance, via memmove. From the term memmove, I can guess that it has to be a memory copy and it might not even know where this copy is being done. And hence, no method of the Ctypes-Structure class will be called. Is that correct? – Raj Kumar Feb 09 '15 at 15:59
-
Ok, to explain my problem little more, I'm having some enum variables inside my ctype-structures. Since, enumeration is not supported by ctypes, I got a piece of code from http://code.activestate.com/recipes/576415-ctype-enumeration-class/ . The problem with this part is, since I'm doing a memmove of data over the structure this enum belogs to, the __init__ method of class Enumeration(c_uint) is not at all called. So, I'm unable to get the enum definition of the value that is assigned to now. – Raj Kumar Feb 09 '15 at 16:04
-
Correct - this is directly replacing some memory "behind the curtains". So you need to declare some auxiliary methods on your struct that perform the necessary operations after the memmove. – deets Feb 09 '15 at 16:29
-
Hmm thanks :-) Is it possible to find how this part is done for other data-types? For ex, what would have happened to the c_uint variables that i have in the structure? How is that, they are giving me the correct number? For this enum-object, if I can get the value that is assigned to it, then I can get the corresponding string. Here is how my structure looks like class headerStruct(Structure): _fields_ = [("param1", EBoolean),("param2", c_uint)]. When i do a getattr for param2, I'm getting the correct integer that is copied. But the same is not happening for param1. – Raj Kumar Feb 09 '15 at 16:39
-
You mean, I cannot use that? – Raj Kumar Feb 09 '15 at 18:00
-
1@RajKumar: you may want to [edit] your question now that it has become clear what actual problem you were facing. – Martijn Pieters Feb 09 '15 at 18:47
-
@ Martijn Pieters: Sure. Changing it now. – Raj Kumar Feb 09 '15 at 19:01
3 Answers
The Enumeration class suggested by Raj Kumar was broken in that it required the __init__
to be run to set a new value in a variable, and thus unusable if the value was changed on C side. Here is a fixed version thereof:
class EnumerationType(type(c_uint)):
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(c_uint).__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__
class CEnumeration(c_uint):
__metaclass__ = EnumerationType
_members_ = {}
def __repr__(self):
value = self.value
return "<%s.%s: %d>" % (
self.__class__.__name__,
self._reverse_map_.get(value, '(unknown)'),
value
)
def __eq__(self, other):
if isinstance(other, (int, long)):
return self.value == other
return type(self) == type(other) and self.value == other.value
Now one can declare a CEnumeration
:
class EBoolean(CEnumeration):
FALSE = 0
TRUE = 1
and use it:
class HeaderStruct(Structure):
_fields_ = [("param1", EBoolean),
("param2", c_uint)]
Examples:
>>> header = HeaderStruct()
>>> header.param1
<EBoolean.FALSE: 0>
>>> memmove(addressof(header), b'\x01', 1) # write LSB 0x01 in the boolean
>>> header.param1
<EBoolean.TRUE: 1>
>>> header.param1 == EBoolean.TRUE
True
>>> header.param1 == 1 # as a special case compare against ints
True
>>> header.param1.value
1L

- 129,958
- 22
- 279
- 321
-
Hi Thanks for the answer. But can you please explain it a bit more?? As of now, the only way for assigning this variable is via memmove. And after I do this, I'm able to read the value assigned to other 'c_uint' variables in the structures using getattr(). I'm not sure of what method to call in this object to get the value assigned to this variable in the ctypes-structure. – Raj Kumar Feb 09 '15 at 16:52
-
Thanks a ton.. This is precisely what I wanted. Thanks again for the time you spent in correcting this :-) – Raj Kumar Feb 09 '15 at 18:55
-
Instead of calling `memmove` you can usually use `header = HeaderStruct.from_buffer(readBuffer)`, which avoids the copy if the buffer is writable. For a copy (or for a read-only buffer) use `HeaderStruct.from_buffer_copy(readBuffer)`. – Eryk Sun Feb 09 '15 at 20:37
-
The above comment isn't directed at you, per se. I upvoted your answer. I wish the enum module of 3.x made this easier, but `enum.EnumMeta` leads to a metaclass conflict. – Eryk Sun Feb 10 '15 at 10:54
-
No offence was taken. I did spend quite a time with the enum34 on python 2.7 trying to get it to work properly, but I reached the same conclusion: one would have to fork the whole module to make it work with `PyCSimpleType`, which would have made too long an answer. – Antti Haapala -- Слава Україні Feb 10 '15 at 11:09
-
Just a minor issue that should be noted, but `globals()` is not relative to the module the `CEnumeration` subclass (eg `EBoolean`) is defined in, but rather the module `EnumerationType` is defined in... All entry definitions in any subclasses will be defined there. – Tcll Mar 04 '19 at 13:19
-
please consider using `sys._getframe(1).f_globals.update(_members_)` (and remember to delete the frame), or use inspect for a more pythonic approach. – Tcll Mar 04 '19 at 13:36
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

- 63
- 3
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]

- 3,129
- 3
- 14
- 28