Why is there so much muck in my program???Posted: April 25, 2012
In order to do some really good programming, the kind where you get to focus on your core competencies, requires that you have libraries and frameworks that are very easy to use, and hit the spot just right in terms of complexity and power.
Since I have been doing a lot of interop to various libraries, I’m seeing first hand how complex this can be, and how evil and wrong some libraries can be, and how beautifully simple others are. One of the conclusions I have come to, is for modern day programming, a library that has a brain dead straight simple C interface is the best possible way to go. Not all C interfaces are created equally though. The ones I like typically look like this:
int getlasterror(); context_t ctx = create_context(params); func1(context_t ctx); func2(context_t ctc, int param1);
That is, create some sort of handle, that the library of routines understands, and will do something with. Then, call various routines in the library, passing in the context handle that you were given by the library in the beginning.
Somewhat similar, but extremely unsatisfactory is this pattern:
context_t ctx; int err = create_context(&ctx);
I don’t like this because I have to come up with a mechanism to pass in a pointer to be filled in. Well, depending on the language that I’m using, passing in a pointer to something may or may not be possible, so it’s just a couple bits harder to do interop with than the first mechanism. The one positive tradeoff here is that the function can have a return value that you can check immediately, instead of having to call something like “getlasterror()” that the first mechanism utilizes. This is useful in an environment where youi might be doing multi-threading, but since I can handle multi-threading in other ways, this is not a big bonus for me.
The Windows COM environment is full of this second mechanism, only it goes even further, which brings me to the next pattern.
objectinterface *objectptr; int err = getobject(&objectptr); *objectPtr->vtbl->func(params);
In this case, the library tries to do too many things on my behalf. It’s trying to return an “object” to me, which is a structure with a ‘vtable’, which has pointers to functions, which I can then call. This is just plain hard and ugly. I might not be able to deal with these function pointers from my language, and that passing in a pointer to a pointer thing is always kind of problematic, even from C (at least for me). In this case, I’d actually be happy with using the first method, and creating my own class construct appropriately for my preferred language. In the case of Lua, this would be a snap, as LuaJIT FFI will give me ready access to the function signatures, and I can create a “class” fairly easily using a table. By trying to use this last method instead, I’m just in for a world of hurt, pain and suffering, and will spend most of my time doing interop rather than actually using the library in question.
But, all is not well, just because you have a straight C interface to your library. I was recently trying to get the OpenGL interop working “perfectly”. Basically, I wanted to have the option of constructing a GL context for any given version, and being specific about the pixel attributes and other things.
Well, GL has this funky dance you have to do in order to create a proper context. That dance involves creating a fake window, a fake context, getting pixel attributes, creating your real window, setting pixel format on that window, destroying the old fake window, getting the GL context. All told, it’s about 100 lines of convoluted code, that is very finicky, and sensitive to the order in which things are done. There is esoterica at many levels as depending on which version of a GL context you’re trying to construct, things get very hairy as to defaults, what behaviors you’ll get, etc.
But, now that the fragile mess is done, I can forget about that part and just continue using OpenGL happily. Luckily, OpenGL interfaces are of the first variety. I am free to create my own class constructs where I feel I need them, and don’t bother in places where I don’t. Thank goodness, otherwise, I’d hate to think how life would have been if I had to swallow the whole of OpenInventor just to get some triangles displayed on the screen.
I can only hope that anyone creating a modern library that is meant to be used by dynamic languages, will fight the urge to provide too much, and will just go back to some basic C roots, as that truly is a universal and simple base to start from.