The C-centric design philosophy makes CFFI a pain to use
Ok, this is going to be more of a rant about the overall design philosophy of CFFI, moreso than a specific feature request. More of a general "design smell" complaint. Please bear with me while I get some gripes off my chest.
First, I want to say I love CFFI for what it is on the surface. It makes wrapping C libraries simple and fast. But it involves a lot of repetitive typing and boiler-plate to get up and running, much of which could easily be avoided with just a little bit of pythonic introspection. And yes, I KNOW "you can't do that in C either". But that's the point! That's why I'm writing in python instead of C! :P
People program in python because of the high-level nature of the language. Introspection and a dynamic coding style are some of the most important strengths of python over lower-level languages. Sure, the syntax is nice and clean, and the standard library is very convenient, but most of that is icing on the cake. The real power comes from being able to dynamically manipulate objects at runtime and inspect objects to alter behavior without a lot of intermediate boilder-plate or near-duplicate code.
But the default design philosophy of CFFI, it seems, has been essentially "make it as difficult and inflexible as C wherever possible."
For example, there's no clear way to be able to tell if an object is a CFFI datatype. There is no clear class which can be passed to insinstance(). If you look at
myctype.__class__ you get
_cffi_backend.CData. Of course, this doesn't actually seem to exist anywhere. The closest I could find is
cffi.FFI.CData. Why is this a class attribute instead of something easily found in the cffi module? Of course, this doesn't appear to be documented anywhere, you just have to
dir() around in cffi to find it.
In #pypy on Freenode, it was suggested I could do
ffi.typeof(obj) to test if it's a CData object, but that just raises a TypeError if it's not a CFFI object. Seems like overkill to have a try/except just to check the type of something.
Or, the lack of any way to get the type, argument list, etc from a CFFI C function. You have to have a reference to the original FFI instance to do
ffi.typeof(myfunc). (I wrote a function which accepts a verifier object, does a dir() on it, and adds a .ffi instance attr to all the funcions, just so I could have a reference to the ffi object and do "
myfunc.ffi.typeof(myfunc)" which just feels wrong...)
Oh, and this brings me to my next point. Want to write the same wrapper code for all your C structs? Well, you can either copy-and-paste the same boiler-plate for each struct, or iterate over the values in the
myffi._parser._declarations dict. And the keys in this dict are useless because they're like
"struct My_Somestruct" instead of just
"My_Somestruct", so you either have to parse/mangle that string yourself, or do
for ctype in myffi.itervalues(): if isinstance(ctype, (cffi.model.StructType, cffi.model.UnionType)): cobjs[ctype.get_c_name()] = CStructType(ffi, ctype)
(Actual code from my project. And again, most of this is undocumented, just have to
dir() around to find it...)
Also, I tried opening a feature request a while ago asking that the automatic type coersion for passing Python objects to C functions be extended in a generic way (issue
#101) but it was rejected because "you can't do that in C either". Just seems like a pointless design philosophy to me. I'm not expecting a fully ctypes-level of pythonicness, but some accomodation for, you know, python programmers, would be nice.