Commits

Armin Rigo committed 4aed2f4 Merge

hg merge enum-as-int

  • Participants
  • Parent commits 62db8cc, ea9fec0

Comments (0)

Files changed (7)

c/_cffi_backend.c

     return (PyObject *)cd;
 }
 
-static PyObject *convert_enum_string_to_int(CTypeDescrObject *ct, PyObject *ob)
-{
-    PyObject *d_value;
-    char *p = PyText_AsUTF8(ob);
-    if (p == NULL)
-        return NULL;
-
-    if (p[0] == '#') {
-        char *number = p + 1;       /* strip initial '#' */
-        PyObject *ob2 = PyText_FromString(number);
-        if (ob2 == NULL)
-            return NULL;
-
-        d_value = PyNumber_Long(ob2);
-        Py_DECREF(ob2);
-    }
-    else {
-        d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 0), ob);
-        if (d_value == NULL) {
-            PyErr_Format(PyExc_ValueError,
-                         "'%s' is not an enumerator for %s",
-                         p, ct->ct_name);
-            return NULL;
-        }
-        Py_INCREF(d_value);
-    }
-    return d_value;
-}
-
 static CDataObject *_new_casted_primitive(CTypeDescrObject *ct);  /*forward*/
 
 static PyObject *
         PY_LONG_LONG value;
         /*READ(data, ct->ct_size)*/
         value = read_raw_signed_data(data, ct->ct_size);
-
-        if (ct->ct_flags & CT_IS_ENUM) {
-            PyObject *d_value, *d_key = PyInt_FromLong((int)value);
-            if (d_key == NULL)
-                return NULL;
-
-            d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key);
-            Py_DECREF(d_key);
-            if (d_value != NULL)
-                Py_INCREF(d_value);
-            else
-                d_value = PyText_FromFormat("#%d", (int)value);
-            return d_value;
-        }
-        else if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
+        if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG)
             return PyInt_FromLong((long)value);
         else
             return PyLong_FromLongLong(value);
     }
     if (ct->ct_flags & CT_PRIMITIVE_SIGNED) {
         PY_LONG_LONG value = _my_PyLong_AsLongLong(init);
-
-        if (value == -1 && PyErr_Occurred()) {
-            if (!(ct->ct_flags & CT_IS_ENUM))
-                return -1;
-            else {
-                PyObject *ob;
-                PyErr_Clear();
-                if (!PyTextAny_Check(init)) {
-                    expected = "str or int";
-                    goto cannot_convert;
-                }
-
-                ob = convert_enum_string_to_int(ct, init);
-                if (ob == NULL)
-                    return -1;
-                value = PyLong_AsLongLong(ob);
-                Py_DECREF(ob);
-                if (value == -1 && PyErr_Occurred())
-                    return -1;
-            }
-        }
+        if (value == -1 && PyErr_Occurred())
+            return -1;
         write_raw_integer_data(buf, value, ct->ct_size);
         if (value != read_raw_signed_data(buf, ct->ct_size))
             goto overflow;
 
 static PyObject *cdata_float(CDataObject *cd);  /*forward*/
 
+static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both)
+{
+    PyObject *d_key, *d_value;
+    CTypeDescrObject *ct = cd->c_type;
+
+    assert(ct->ct_flags & CT_IS_ENUM);
+    d_key = convert_to_object(cd->c_data, ct);
+    if (d_key == NULL)
+        return NULL;
+
+    d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key);
+    if (d_value != NULL) {
+        if (both) {
+            PyObject *o = PyObject_Str(d_key);
+            if (o == NULL)
+                d_value = NULL;
+            else {
+                d_value = PyText_FromFormat("%s: %s",
+                                            PyText_AS_UTF8(o),
+                                            PyText_AS_UTF8(d_value));
+                Py_DECREF(o);
+            }
+        }
+        else
+            Py_INCREF(d_value);
+    }
+    else
+        d_value = PyObject_Str(d_key);
+    Py_DECREF(d_key);
+    return d_value;
+}
+
 static PyObject *cdata_repr(CDataObject *cd)
 {
     char *extra;
     PyObject *result, *s;
 
     if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
-        if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
-            PyObject *o = convert_to_object(cd->c_data, cd->c_type);
-            if (o == NULL)
-                return NULL;
-            s = PyObject_Repr(o);
-            Py_DECREF(o);
+        if (cd->c_type->ct_flags & CT_IS_ENUM) {
+            s = convert_cdata_to_enum_string(cd, 1);
         }
-        else {
+        else if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) {
             long double lvalue;
             char buffer[128];   /* big enough */
             /*READ(cd->c_data, sizeof(long double)*/
             sprintf(buffer, "%LE", lvalue);
             s = PyText_FromString(buffer);
         }
+        else {
+            PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+            if (o == NULL)
+                return NULL;
+            s = PyObject_Repr(o);
+            Py_DECREF(o);
+        }
     }
     else if ((cd->c_type->ct_flags & CT_ARRAY) && cd->c_type->ct_length < 0) {
         s = PyText_FromFormat("sliced length %zd", get_array_length(cd));
                                  (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) {
         value = (Py_intptr_t)((CDataObject *)ob)->c_data;
     }
-    else if ((ct->ct_flags & CT_IS_ENUM) && PyTextAny_Check(ob)) {
-        ob = convert_enum_string_to_int(ct, ob);
-        if (ob == NULL)
-            return NULL;
-        cd = cast_to_integer_or_char(ct, ob);
-        Py_DECREF(ob);
-        return cd;
-    }
 #if PY_MAJOR_VERSION < 3
     else if (PyString_Check(ob)) {
         if (PyString_GET_SIZE(ob) != 1) {
                 return -1;
             }
         }
-        if ((ctype->ct_flags & (CT_PRIMITIVE_SIGNED | CT_IS_ENUM))
-                == CT_PRIMITIVE_SIGNED) {
+        if (ctype->ct_flags & CT_PRIMITIVE_SIGNED) {
             PY_LONG_LONG value;
             /* It's probably fine to always zero-extend, but you never
                know: maybe some code somewhere expects a negative
     CTypeDescrObject *td;
     Py_ssize_t i, n;
     struct aligncheck_int { char x; int y; };
+    struct aligncheck_long { char x; long y; };
+    long smallest_item = 0;
+    unsigned long largest_item = 0;
+    int size, flags;
 
     if (!PyArg_ParseTuple(args, "sO!O!:new_enum_type",
                           &ename,
     dict1 = PyDict_New();
     if (dict1 == NULL)
         goto error;
+    dict2 = PyDict_New();
+    if (dict2 == NULL)
+        goto error;
+
     for (i=n; --i >= 0; ) {
         long lvalue;
+        unsigned long ulvalue;
         PyObject *value = PyTuple_GET_ITEM(enumvalues, i);
         tmpkey = PyTuple_GET_ITEM(enumerators, i);
         Py_INCREF(tmpkey);
             }
         }
         lvalue = PyLong_AsLong(value);
-        if ((lvalue == -1 && PyErr_Occurred()) || lvalue != (int)lvalue) {
-            PyErr_Format(PyExc_OverflowError,
-                         "enum '%s' declaration for '%s' does not fit an int",
-                         ename, PyText_AS_UTF8(tmpkey));
-            goto error;
+        if (PyErr_Occurred()) {
+            PyErr_Clear();
+            ulvalue = PyLong_AsUnsignedLong(value);
+            if (PyErr_Occurred()) {
+                PyErr_Format(PyExc_OverflowError,
+                             "enum '%s' declaration for '%s' does not fit "
+                             "a long or unsigned long",
+                             ename, PyText_AS_UTF8(tmpkey));
+                goto error;
+            }
+            if (ulvalue > largest_item)
+                largest_item = ulvalue;
+        }
+        else {
+            if (lvalue < 0) {
+                if (lvalue < smallest_item)
+                    smallest_item = lvalue;
+            }
+            else {
+                ulvalue = (unsigned long)lvalue;
+                if (ulvalue > largest_item)
+                    largest_item = ulvalue;
+            }
         }
         if (PyDict_SetItem(dict1, tmpkey, value) < 0)
             goto error;
+        if (PyDict_SetItem(dict2, value, tmpkey) < 0)
+            goto error;
         Py_DECREF(tmpkey);
         tmpkey = NULL;
     }
 
-    dict2 = PyDict_New();
-    if (dict2 == NULL)
-        goto error;
-    for (i=n; --i >= 0; ) {
-        if (PyDict_SetItem(dict2, PyTuple_GET_ITEM(enumvalues, i),
-                                  PyTuple_GET_ITEM(enumerators, i)) < 0)
+    if (smallest_item < 0) {
+        flags = CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_FITS_LONG | CT_IS_ENUM;
+        if (smallest_item == (int)smallest_item &&
+                 largest_item <= (unsigned long)INT_MAX) {
+            size = sizeof(int);
+        }
+        else if (largest_item <= (unsigned long)LONG_MAX) {
+            size = sizeof(long);
+        }
+        else {
+            PyErr_Format(PyExc_OverflowError,
+                         "enum '%s' values don't all fit into either 'long' "
+                         "or 'unsigned long'", ename);
             goto error;
+        }
+    }
+    else if (sizeof(unsigned int) < sizeof(unsigned long) &&
+             largest_item == (unsigned int)largest_item) {
+        flags = CT_PRIMITIVE_UNSIGNED | CT_PRIMITIVE_FITS_LONG | CT_IS_ENUM;
+        size = sizeof(unsigned int);
+    }
+    else {
+        flags = CT_PRIMITIVE_UNSIGNED | CT_IS_ENUM;
+        size = sizeof(unsigned long);
     }
 
     combined = PyTuple_Pack(2, dict1, dict2);
     Py_CLEAR(dict2);
     Py_CLEAR(dict1);
 
-    switch (sizeof(int)) {
+    switch (size) {
     case 4: ffitype = &ffi_type_sint32; break;
     case 8: ffitype = &ffi_type_sint64; break;
-    default: Py_FatalError("'int' is not 4 or 8 bytes");
+    default: Py_FatalError("'int' or 'long' is not 4 or 8 bytes"); return NULL;
     }
 
     name_size = strlen("enum ") + strlen(ename) + 1;
     memcpy(td->ct_name, "enum ", strlen("enum "));
     memcpy(td->ct_name + strlen("enum "), ename, name_size - strlen("enum "));
     td->ct_stuff = combined;
-    td->ct_size = sizeof(int);
-    td->ct_length = offsetof(struct aligncheck_int, y);
+    td->ct_size = size;
+    td->ct_length = size == sizeof(int) ? offsetof(struct aligncheck_int, y)
+                                        : offsetof(struct aligncheck_long, y);
     td->ct_extra = ffitype;
-    td->ct_flags = CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_FITS_LONG | CT_IS_ENUM;
+    td->ct_flags = flags;
     td->ct_name_position = name_size - 1;
     return (PyObject *)td;
 
 #endif
     }
     else if (cd->c_type->ct_flags & CT_IS_ENUM) {
-        return convert_to_object(cd->c_data, cd->c_type);
+        return convert_cdata_to_enum_string(cd, 0);
     }
     else if (cd->c_type->ct_flags & CT_IS_BOOL) {
         /* fall through to TypeError */
 def test_cast_to_enum():
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
     e = cast(BEnum, 0)
-    assert repr(e) == "<cdata 'enum foo' 'def'>"
+    assert repr(e) == "<cdata 'enum foo' 0: def>"
+    assert repr(cast(BEnum, -42)) == "<cdata 'enum foo' -42>"
+    assert repr(cast(BEnum, -20)) == "<cdata 'enum foo' -20: ab>"
     assert string(e) == 'def'
     assert string(cast(BEnum, -20)) == 'ab'
-    assert string(cast(BEnum, 'c')) == 'c'
-    assert int(cast(BEnum, 'c')) == 1
-    assert int(cast(BEnum, 'def')) == 0
+    assert int(cast(BEnum, 1)) == 1
+    assert int(cast(BEnum, 0)) == 0
     assert int(cast(BEnum, -242 + 2**128)) == -242
-    assert string(cast(BEnum, -242 + 2**128)) == '#-242'
-    assert string(cast(BEnum, '#-20')) == 'ab'
-    assert repr(cast(BEnum, '#-20')) == "<cdata 'enum foo' 'ab'>"
-    assert repr(cast(BEnum, '#-21')) == "<cdata 'enum foo' '#-21'>"
+    assert string(cast(BEnum, -242 + 2**128)) == '-242'
+    #
+    BEnum = new_enum_type("bar", ('def', 'c', 'ab'), (0, 1, 20))
+    e = cast(BEnum, -1)
+    assert repr(e) == "<cdata 'enum bar' 4294967295>"     # unsigned int
 
 def test_enum_with_non_injective_mapping():
     BEnum = new_enum_type("foo", ('ab', 'cd'), (7, 7))
     e = cast(BEnum, 7)
-    assert repr(e) == "<cdata 'enum foo' 'ab'>"
+    assert repr(e) == "<cdata 'enum foo' 7: ab>"
     assert string(e) == 'ab'
 
 def test_enum_in_struct():
     BStructPtr = new_pointer_type(BStruct)
     complete_struct_or_union(BStruct, [('a1', BEnum, -1)])
     p = newp(BStructPtr, [-20])
-    assert p.a1 == "ab"
-    p = newp(BStructPtr, ["c"])
-    assert p.a1 == "c"
+    assert p.a1 == -20
+    p = newp(BStructPtr, [12])
+    assert p.a1 == 12
     e = py.test.raises(TypeError, newp, BStructPtr, [None])
-    assert "must be a str or int, not NoneType" in str(e.value)
+    assert "an integer is required" in str(e.value)
+    py.test.raises(TypeError, 'p.a1 = "def"')
     if sys.version_info < (3,):
-        p.a1 = unicode("def")
-        assert p.a1 == "def" and type(p.a1) is str
-        py.test.raises(UnicodeEncodeError, "p.a1 = unichr(1234)")
         BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,))
-        assert string(cast(BEnum2, unicode('abc'))) == 'abc'
+        assert string(cast(BEnum2, 5)) == 'abc'
+        assert type(string(cast(BEnum2, 5))) is str
 
 def test_enum_overflow():
-    for ovf in (2**63, -2**63-1, 2**31, -2**31-1):
-        e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
-                           (5, ovf))
-        assert str(e.value) == (
-            "enum 'foo' declaration for 'b' does not fit an int")
+    max_uint = 2 ** (size_of_int()*8) - 1
+    max_int = max_uint // 2
+    max_ulong = 2 ** (size_of_long()*8) - 1
+    max_long = max_ulong // 2
+    # 'unsigned int' case
+    e = new_enum_type("foo", ('a', 'b'), (0, 3))
+    assert sizeof(e) == size_of_int()
+    assert int(cast(e, -1)) == max_uint     # 'e' is unsigned
+    e = new_enum_type("foo", ('a', 'b'), (0, max_uint))
+    assert sizeof(e) == size_of_int()
+    assert int(cast(e, -1)) == max_uint
+    assert e.elements == {0: 'a', max_uint: 'b'}
+    assert e.relements == {'a': 0, 'b': max_uint}
+    # 'signed int' case
+    e = new_enum_type("foo", ('a', 'b'), (-1, max_int))
+    assert sizeof(e) == size_of_int()
+    assert int(cast(e, -1)) == -1
+    assert e.elements == {-1: 'a', max_int: 'b'}
+    assert e.relements == {'a': -1, 'b': max_int}
+    e = new_enum_type("foo", ('a', 'b'), (-max_int-1, max_int))
+    assert sizeof(e) == size_of_int()
+    assert int(cast(e, -1)) == -1
+    assert e.elements == {-max_int-1: 'a', max_int: 'b'}
+    assert e.relements == {'a': -max_int-1, 'b': max_int}
+    # 'unsigned long' case
+    e = new_enum_type("foo", ('a', 'b'), (0, max_long))
+    assert sizeof(e) == size_of_long()
+    assert int(cast(e, -1)) == max_ulong     # 'e' is unsigned
+    e = new_enum_type("foo", ('a', 'b'), (0, max_ulong))
+    assert sizeof(e) == size_of_long()
+    assert int(cast(e, -1)) == max_ulong
+    assert e.elements == {0: 'a', max_ulong: 'b'}
+    assert e.relements == {'a': 0, 'b': max_ulong}
+    # 'signed long' case
+    e = new_enum_type("foo", ('a', 'b'), (-1, max_long))
+    assert sizeof(e) == size_of_long()
+    assert int(cast(e, -1)) == -1
+    assert e.elements == {-1: 'a', max_long: 'b'}
+    assert e.relements == {'a': -1, 'b': max_long}
+    e = new_enum_type("foo", ('a', 'b'), (-max_long-1, max_long))
+    assert sizeof(e) == size_of_long()
+    assert int(cast(e, -1)) == -1
+    assert e.elements == {-max_long-1: 'a', max_long: 'b'}
+    assert e.relements == {'a': -max_long-1, 'b': max_long}
+    # overflow: both negative items and items larger than max_long
+    e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+                       (-1, max_long + 1))
+    assert str(e.value) == (
+        "enum 'foo' values don't all fit into either 'long' "
+        "or 'unsigned long'")
+    # overflow: items smaller than -max_long-1
+    e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+                       (-max_long-2, 5))
+    assert str(e.value) == (
+        "enum 'foo' declaration for 'a' does not fit a long or unsigned long")
+    # overflow: items larger than max_ulong
+    e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+                       (5, max_ulong+1))
+    assert str(e.value) == (
+        "enum 'foo' declaration for 'b' does not fit a long or unsigned long")
 
 def test_callback_returning_enum():
     BInt = new_primitive_type("int")
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
     def cb(n):
-        return '#%d' % n
+        if n & 1:
+            return cast(BEnum, n)
+        else:
+            return n
     BFunc = new_function_type((BInt,), BEnum)
     f = callback(BFunc, cb)
-    assert f(0) == 'def'
-    assert f(1) == 'c'
-    assert f(-20) == 'ab'
-    assert f(20) == '#20'
+    assert f(0) == 0
+    assert f(1) == 1
+    assert f(-20) == -20
+    assert f(20) == 20
+    assert f(21) == 21
+
+def test_callback_returning_enum_unsigned():
+    BInt = new_primitive_type("int")
+    BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20))
+    def cb(n):
+        print n
+        if n & 1:
+            return cast(BEnum, n)
+        else:
+            return n
+    BFunc = new_function_type((BInt,), BEnum)
+    f = callback(BFunc, cb)
+    assert f(0) == 0
+    assert f(1) == 1
+    assert f(-21) == 2**32 - 21
+    assert f(20) == 20
+    assert f(21) == 21
 
 def test_callback_returning_char():
     BInt = new_primitive_type("int")

cffi/backend_ctypes.py

 
     def new_enum_type(self, name, enumerators, enumvalues):
         assert isinstance(name, str)
-        mapping = dict(zip(enumerators, enumvalues))
         reverse_mapping = dict(zip(reversed(enumvalues),
                                    reversed(enumerators)))
-        CTypesInt = self.ffi._get_cached_btype(model.PrimitiveType('int'))
-        #
-        def forward_map(source):
-            if not isinstance(source, str):
-                return source
-            try:
-                return mapping[source]
-            except KeyError:
-                if source.startswith('#'):
-                    try:
-                        return int(source[1:])
-                    except ValueError:
-                        pass
-            raise ValueError("%r is not an enumerator for %r" % (
-                source, CTypesEnum))
+        smallest = min(enumvalues or [0])
+        largest = max(enumvalues or [0])
+        if smallest < 0:
+            if largest == ctypes.c_int(largest).value:
+                tp = 'int'
+            elif largest == ctypes.c_long(largest).value:
+                tp = 'long'
+            else:
+                raise OverflowError
+        else:
+            if largest == ctypes.c_uint(largest).value:
+                tp = 'unsigned int'
+            elif largest == ctypes.c_ulong(largest).value:
+                tp = 'unsigned long'
+            else:
+                raise OverflowError
+        CTypesInt = self.ffi._get_cached_btype(model.PrimitiveType(tp))
         #
         class CTypesEnum(CTypesInt):
             __slots__ = []
             _reftypename = 'enum %s &' % name
 
+            def _get_own_repr(self):
+                value = self._value
+                try:
+                    return '%d: %s' % (value, reverse_mapping[value])
+                except KeyError:
+                    return str(value)
+
             def _to_string(self, maxlen):
-                return str(CTypesEnum._from_ctypes(self._value))
-
-            @classmethod
-            def _cast_from(cls, source):
-                source = forward_map(source)
-                return super(CTypesEnum, cls)._cast_from(source)
-
-            @staticmethod
-            def _to_ctypes(x):
-                x = forward_map(x)
-                return CTypesInt._to_ctypes(x)
-
-            @staticmethod
-            def _from_ctypes(value):
-                value = CTypesInt._from_ctypes(value)
+                value = self._value
                 try:
                     return reverse_mapping[value]
                 except KeyError:
-                    return '#%s' % value
+                    return str(value)
         #
         CTypesEnum._fix_class()
         return CTypesEnum

doc/source/index.rst

 -------------------
 
 ``ffi.include(other_ffi)``: includes the typedefs, structs, unions and
-enums defined in another FFI instance.  Usage is similar to a
+enum types defined in another FFI instance.  Usage is similar to a
 ``#include`` in C, where a part of the program might include types
 defined in another part for its own usage.  Note that the include()
 method has no effect on functions, constants and global variables, which
   byte string or unicode string.  (Note that in some situation a single
   wchar_t may require a Python unicode string of length 2.)
 
-- If 'cdata' is an enum, returns the value of the enumerator as a
-  string, or ``#NUMBER`` if the value is out of range.
+- If 'cdata' is an enum, returns the value of the enumerator as a string.
+  If the value is out of range, it is simply returned as the stringified
+  integer.
 
 
 ``ffi.buffer(cdata, [size])``: return a buffer object that references
   length 0, allocating a ``char[]`` of the correct size, and casting
   it to a struct pointer)
 
-* Enum types are always ``int``.  GCC supports enums containing
-  larger constants (``unsigned int``, or ``long long``) as an extension
-  to the C standard, but CFFI does not.  Use
-  ``typedef <exact type> my_enum;`` and then some ``#define foo <value>``.
-
 .. versionadded:: 0.4
    Now supported: the common GCC extension of anonymous nested
    structs/unions inside structs/unions.
 
+.. versionadded:: 0.6
+   Enum types follow the GCC rules: they are defined as the first of
+   ``unsigned int``, ``int``, ``unsigned long`` or ``long`` that fits
+   all numeric values.  Note that the first choice is unsigned.  In CFFI
+   0.5 and before, it was always ``int``.
+
 
 Debugging dlopen'ed C libraries
 -------------------------------
 |    C type     |   writing into         | reading from     |other operations|
 +===============+========================+==================+================+
 |   integers    | an integer or anything | a Python int or  | int()          |
-|               | on which int() works   | long, depending  |                |
-|               | (but not a float!).    | on the type      |                |
+|   and enums   | on which int() works   | long, depending  |                |
+|   `(*****)`   | (but not a float!).    | on the type      |                |
 |               | Must be within range.  |                  |                |
 +---------------+------------------------+------------------+----------------+
 |   ``char``    | a string of length 1   | a string of      | int()          |
 | union         | same as struct, but    |                  | read/write     |
 |               | with at most one field |                  | fields         |
 +---------------+------------------------+------------------+----------------+
-| enum          | an integer, or the enum| the enum value   | int()          |
-|               | value as a string or   | as a string, or  |                |
-|               | as ``"#NUMBER"``       | ``"#NUMBER"``    |                |
-|               |                        | if out of range  |                |
-+---------------+------------------------+------------------+----------------+
 
 .. versionchanged:: 0.3
    `(*)` Note that when calling a function, as per C, a ``item *`` argument
    C.  As for slice assignment, it accepts any iterable, including a list
    of items or another array-like cdata object, but the length must match.
 
+.. versionchanged:: 0.6
+   `(*****)` Enums are now handled like ints (unsigned or signed, int or
+   long, like GCC; note that the first choice is unsigned).  In previous
+   versions, you would get the enum's value as a string.  Now we follow the C
+   convention and treat them as really equivalent to integers.  To compare
+   their value symbolically, use code like ``if x.field == lib.FOO``.
+   If you really want to get their value as a string, use
+   ``ffi.string(ffi.cast("the_enum_type", x.field))``.
+
 
 Reference: verifier
 -------------------

testing/backend_tests.py

     def test_enum(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("enum foo { A, B, CC, D };")
-        assert int(ffi.cast("enum foo", "A")) == 0
-        assert int(ffi.cast("enum foo", "B")) == 1
-        assert int(ffi.cast("enum foo", "CC")) == 2
-        assert int(ffi.cast("enum foo", "D")) == 3
-        ffi.cdef("enum bar { A, B=-2, CC, D };")
-        assert int(ffi.cast("enum bar", "A")) == 0
-        assert int(ffi.cast("enum bar", "B")) == -2
-        assert int(ffi.cast("enum bar", "CC")) == -1
-        assert int(ffi.cast("enum bar", "D")) == 0
-        assert ffi.cast("enum bar", "B") != ffi.cast("enum bar", "B")
-        assert ffi.cast("enum foo", "A") != ffi.cast("enum bar", "A")
-        assert ffi.cast("enum bar", "A") != ffi.cast("int", 0)
-        assert repr(ffi.cast("enum bar", "CC")) == "<cdata 'enum bar' 'CC'>"
-        py.test.raises(ValueError, ffi.cast, "enum bar", "UNKNOWN")
+        assert ffi.string(ffi.cast("enum foo", 0)) == "A"
+        assert ffi.string(ffi.cast("enum foo", 2)) == "CC"
+        assert ffi.string(ffi.cast("enum foo", 3)) == "D"
+        assert ffi.string(ffi.cast("enum foo", 4)) == "4"
+        ffi.cdef("enum bar { A, B=-2, CC, D, E };")
+        assert ffi.string(ffi.cast("enum bar", 0)) == "A"
+        assert ffi.string(ffi.cast("enum bar", -2)) == "B"
+        assert ffi.string(ffi.cast("enum bar", -1)) == "CC"
+        assert ffi.string(ffi.cast("enum bar", 1)) == "E"
+        assert ffi.cast("enum bar", -2) != ffi.cast("enum bar", -2)
+        assert ffi.cast("enum foo", 0) != ffi.cast("enum bar", 0)
+        assert ffi.cast("enum bar", 0) != ffi.cast("int", 0)
+        assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC>"
+        assert repr(ffi.cast("enum foo", -1)) == (  # enums are unsigned, if
+            "<cdata 'enum foo' 4294967295>")        # they contain no neg value
         ffi.cdef("enum baz { A=0x1000, B=0x2000 };")
-        assert int(ffi.cast("enum baz", "A")) == 0x1000
-        assert int(ffi.cast("enum baz", "B")) == 0x2000
+        assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A"
+        assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B"
 
     def test_enum_in_struct(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("enum foo { A, B, C, D }; struct bar { enum foo e; };")
         s = ffi.new("struct bar *")
         s.e = 0
-        assert s.e == "A"
-        s.e = "D"
-        assert s.e == "D"
-        assert s[0].e == "D"
-        s[0].e = "C"
-        assert s.e == "C"
-        assert s[0].e == "C"
+        assert s.e == 0
+        s.e = 3
+        assert s.e == 3
+        assert s[0].e == 3
+        s[0].e = 2
+        assert s.e == 2
+        assert s[0].e == 2
         s.e = ffi.cast("enum foo", -1)
-        assert s.e == '#-1'
-        assert s[0].e == '#-1'
+        assert s.e == 4294967295
+        assert s[0].e == 4294967295
         s.e = s.e
-        py.test.raises(TypeError, "s.e = None")
+        py.test.raises(TypeError, "s.e = 'B'")
+        py.test.raises(TypeError, "s.e = '2'")
+        py.test.raises(TypeError, "s.e = '#2'")
+        py.test.raises(TypeError, "s.e = '#7'")
 
     def test_enum_non_contiguous(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("enum foo { A, B=42, C };")
-        assert int(ffi.cast("enum foo", "A")) == 0
-        assert int(ffi.cast("enum foo", "B")) == 42
-        assert int(ffi.cast("enum foo", "C")) == 43
         assert ffi.string(ffi.cast("enum foo", 0)) == "A"
         assert ffi.string(ffi.cast("enum foo", 42)) == "B"
         assert ffi.string(ffi.cast("enum foo", 43)) == "C"
         invalid_value = ffi.cast("enum foo", 2)
         assert int(invalid_value) == 2
-        assert ffi.string(invalid_value) == "#2"
+        assert ffi.string(invalid_value) == "2"
 
     def test_array_of_struct(self):
         ffi = FFI(backend=self.Backend())
     def test_enum_with_non_injective_mapping(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };")
-        e = ffi.cast("enum e", 'CC')
+        e = ffi.cast("enum e", 0)
         assert ffi.string(e) == "AA"     # pick the first one arbitrarily
 
     def test_nested_anonymous_struct(self):

testing/test_unicode_literals.py

 def test_enum():
     ffi = FFI()
     ffi.cdef("enum foo_e { AA, BB, CC };")        # unicode literal
-    x = ffi.cast("enum foo_e", "BB")
+    x = ffi.cast("enum foo_e", 1)
     assert int(ffi.cast("int", x)) == 1
 
 def test_dlopen():

testing/test_verify.py

     ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };")
     py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2')
     ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
-    assert int(ffi.cast('enum ee', 'EE2')) == 11
-    assert int(ffi.cast('enum ee', 'EE3')) == -10
-    py.test.raises(ValueError, ffi.cast, 'enum ee', '__dotdotdot0__')
+    assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
+    assert ffi.string(ffi.cast('enum ee', -10)) == "EE3"
     #
     # try again
     ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
-    assert int(ffi.cast('enum ee', 'EE2')) == 11
+    assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
 
 def test_full_enum():
     ffi = FFI()
     lib = ffi.verify("enum ee { EE1, EE2, EE3, EE4 };")
     assert lib.EE3 == 2
 
+def test_enum_usage():
+    ffi = FFI()
+    ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+    lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;")
+    assert lib.EE2 == 1
+    s = ffi.new("sp", [lib.EE2])
+    assert s.x == 1
+    s.x = 17
+    assert s.x == 17
+
 def test_nonfull_enum_syntax2():
     ffi = FFI()
     ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };")
     py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
     ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
-    assert int(ffi.cast('enum ee', 'EE2')) == 11
-    assert int(ffi.cast('enum ee', 'EE3')) == -10
+    assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
+    assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3'
     #
     ffi = FFI()
     ffi.cdef("enum ee { EE1, EE2=\t... };")
     py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1')
     ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
-    assert int(ffi.cast('enum ee', 'EE2')) == 11
+    assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2'
     #
     ffi = FFI()
     ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };")
     ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ")
-    assert int(ffi.cast('enum ee2', 'EE4')) == -1239
-    assert int(ffi.cast('enum ee2', 'EE5')) == -1238
+    assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4'
+    assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5'
 
 def test_get_set_errno():
     ffi = FFI()
         int foo_func(enum foo_e e) { return e; }
     """)
     assert lib.foo_func(lib.BB) == 2
-    assert lib.foo_func("BB") == 2
+    py.test.raises(TypeError, lib.foo_func, "BB")
 
 def test_enum_as_function_result():
     ffi = FFI()
         enum foo_e { AA, CC, BB };
         enum foo_e foo_func(int x) { return x; }
     """)
-    assert lib.foo_func(lib.BB) == "BB"
+    assert lib.foo_func(lib.BB) == lib.BB == 2
 
 def test_enum_values():
     ffi = FFI()
     ffi = FFI()
     ffi.cdef("typedef enum { AA, BB, ... } enum1_t;")
     lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;")
-    assert ffi.string(ffi.cast("enum1_t", 1)) == '#1'
+    assert ffi.string(ffi.cast("enum1_t", 1)) == '1'
     assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB'
     assert lib.AA == 0
     assert lib.BB == 2
         typedef enum { AA, CC, BB } foo_t;
         foo_t foo_func(int x) { return x; }
     """)
-    assert lib.foo_func(lib.BB) == "BB"
+    assert lib.foo_func(lib.BB) == lib.BB == 2
 
 def test_callback_calling_convention():
     py.test.skip("later")
     ffi2.cdef("int myfunc(enum foo_e);")
     lib2 = ffi2.verify("enum foo_e { CC, BB, AA };"
                        "int myfunc(enum foo_e x) { return (int)x; }")
-    res = lib2.myfunc("AA")
+    res = lib2.myfunc(lib2.AA)
     assert res == 2
 
 def test_string_to_voidp_arg():