At least for GCC enum is just a simple numeric type. It can be 8-, 16-, 32-, 64-bit or whatever (I have tested it with 64-bit values) as well as signed or unsigned. I guess it cannot exceed long long int, but practically you should check the range of your enums and choose something like c_uint.
Here is an example. The C program:
enum batch_op {
OP1 = 2,
OP2 = 3,
OP3 = -1,
};
struct attrl {
char *name;
struct attrl *next;
enum batch_op op;
};
void f(struct attrl *x) {
x->op = OP3;
}
and the Python one:
from ctypes import (Structure, c_char_p, c_uint, c_int,
POINTER, CDLL)
class AttrList(Structure): pass
AttrList._fields_ = [
('name', c_char_p),
('next', POINTER(AttrList)),
('op', c_int),
]
(OP1, OP2, OP3) = (2, 3, -1)
enum = CDLL('./libenum.so')
enum.f.argtypes = [POINTER(AttrList)]
enum.f.restype = None
a = AttrList(name=None, next=None, op=OP2)
assert a.op == OP2
enum.f(a)
assert a.op == OP3
Answer from Andrey Vlasovskikh on Stack OverflowAt least for GCC enum is just a simple numeric type. It can be 8-, 16-, 32-, 64-bit or whatever (I have tested it with 64-bit values) as well as signed or unsigned. I guess it cannot exceed long long int, but practically you should check the range of your enums and choose something like c_uint.
Here is an example. The C program:
enum batch_op {
OP1 = 2,
OP2 = 3,
OP3 = -1,
};
struct attrl {
char *name;
struct attrl *next;
enum batch_op op;
};
void f(struct attrl *x) {
x->op = OP3;
}
and the Python one:
from ctypes import (Structure, c_char_p, c_uint, c_int,
POINTER, CDLL)
class AttrList(Structure): pass
AttrList._fields_ = [
('name', c_char_p),
('next', POINTER(AttrList)),
('op', c_int),
]
(OP1, OP2, OP3) = (2, 3, -1)
enum = CDLL('./libenum.so')
enum.f.argtypes = [POINTER(AttrList)]
enum.f.restype = None
a = AttrList(name=None, next=None, op=OP2)
assert a.op == OP2
enum.f(a)
assert a.op == OP3
Using c_int or c_uint would be fine. Alternatively, there is a recipe in the cookbook for an Enumeration class.
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
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
I'm a bit disappointed to answer to this question myself. Especially since I found it all from the f* manual.
http://docs.python.org/library/ctypes.html#calling-functions-with-your-own-custom-data-types
To complete my answer, I'll write some code that does wrap an item.
from ctypes import CDLL, c_uint, c_char_p
class Flag(object):
flags = [(0x1, 'fun'), (0x2, 'toy')]
@classmethod
def from_param(cls, data):
return c_uint(encode_flags(self.flags, data))
libc = CDLL('libc.so.6')
printf = libc.printf
printf.argtypes = [c_char_p, Flag]
printf("hello %d\n", ["fun", "toy"])
encode_flags transforms that nifty list into an integer.
Why don't you use c_uint for the enum parameter and then use a mapping like this (enums are usually unsigned integer values):
in C:
typedef enum {
MY_VAR = 1,
MY_OTHERVAR = 2
} my_enum_t;
and in Python:
class MyEnum():
__slots__ = ('MY_VAR', 'MY_OTHERVAR')
MY_VAR = 1
MY_OTHERVAR = 2
myfunc.argtypes = [c_uint, ...]
You can then pass MyEnum fields to the function.
If you want a string representation for the enumerated values, you can use a dictionary in the MyEnum class.