Python CFFI enum from string name

Question:

I have an enum defined in Python cffi. How do I instantiate it by name? The docs say how to get the string name from enum, but not how to create it.

ffibuilder = FFI()

ffibuilder.cdef('typedef enum { dense, sparse } dimension_mode;')

dim = ffibuilder.new('dimension_mode', 'sparse')
# E  TypeError: expected a pointer or array ctype, got 'dimension_mode'
Asked By: drhagen

||

Answers:

You need to call dlopen('c') to load your enum into C-namespace.

>>> from cffi import FFI
>>> ffibuilder = FFI()
>>> ffibuilder.cdef('typedef enum { dense, sparse } dimension_mode;')
>>> dim = ffibuilder.new('dimension_mode', 'sparse')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/anaconda3/lib/python3.6/site-packages/cffi/api.py", line 258, in new
    return self._backend.newp(cdecl, init)
TypeError: expected a pointer or array ctype, got 'dimension_mode'

call dlopen():

>>> c = ffibuilder.dlopen('c')

Now, access/assign enum values:

>>> c.dense
0
>>> c.sparse
1
>>>

From ffi docs:

You can use the library object to call the functions previously
declared by ffi.cdef(), to read constants, and to read or write global
variables. Note that you can use a single cdef() to declare functions
from multiple libraries, as long as you load each of them with
dlopen() and access the functions from the correct one.

The libpath is the file name of the shared library, which can contain
a full path or not (in which case it is searched in standard
locations, as described in man dlopen), with extensions or not.
Alternatively, if libpath is None, it returns the standard C library
(which can be used to access the functions of glibc, on Linux).

Answered By: brokenfoot

brokenfoot’s answer is pretty good start but it doesn’t resolve the mistake you made that resulted in a TypeError here; for ‘instantiation’ of the memory as you suggested you’re also using the first parameter to ffi.new incorrectly.

One should specify types to ffi as pointers, so to fully finish the solution:

>>> c = ffi.dlopen('c')
>>> dim = ffi.new('dimension_mode *', c.sparse)
>>> dim
<cdata 'dimension_mode *' owning 4 bytes>
>>> ffi.buffer(dim)[:]
b'x01x00x00x00'

Just to elucidate this is a frighteningly complicated way of doing what brokenfoot is doing to just know the enumeration of ‘sparse’. If you just need the enum values in Python, rather than memory containing the enum values use the library object, not memory.

Just to show the buffer you allocated of the type dimension_mode containing the value c.sparse is equal to c.sparse:

>>> import struct, sys
>>> int.from_bytes(ffi.buffer(dim)[:], sys.byteorder) == c.sparse
True
Answered By: some cat
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.