I'm writing an FFI interface for an existing library (written in C).
The library uses a good number of opaque structures, so I defined a few ExternalStructures
(with no fields) to use as void*
.
Now I've seen two ways (or four?) of interfacing with the library:
Having an ExternalLibrary
with a method per exported function: This could have the method in the instance class, and then use a singleton pattern to have a single instance. Or implement the methods in the class side with the "more complex" syntax including the moduleName
in the FFI pragma like in:
ffiTestFloats: f1 with: f2
"FFITestLibrary ffiTestFloats: $A with: 65.0"
<cdecl: float 'ffiTestFloats' (float float) module:'SqueakFFIPrims'>
^self externalCallFailed
What's better?
Additionally I've seen other way of doing this, not having an ExternalLibrary
at all, and implementing the methods directly in the ExternalStructure
. I like this second part better, however, all the FFI interface definition is spread through several classes, and maintaining and porting to other platforms, Smalltalk dialects or library versions could be more complex.
So, what's the "right" way of doing it?
I would go with the ExternalLibrary
approach since it lets you customize the library name instead of hard-coding it in each method.
I would stick to the traditional approach of modeling things as they are. What we have here is an external library, then let's create a class for it and replicate its API in our object, of course, using instance side methods that perform the required FFI calls.
We've been using this approach for two decades now and the experience has shown that the singleton pattern works very well in this case because it makes the client's life easy. Of course, this facility has to be handled with care so you don't reference the library instance from inappropriate places. Note however that this is not an essential decision, but you will have to somehow keep the unique instance of the library stored somewhere.
Implementing FFI calls in the external structures involved is not natural because some calls might involve more than one structure or none at all. So, where would you put those?
You also mention the idea of implementing the methods in the class side. After all, we all agree in that there should be only one instance of each library, shouldn't it? One reason to discard this possibility is that class side methods would provide a less flexible implementation. Why? Because one thing is to use some mechanism to have only one instance of the class and another is to make it impossible to have it. If your object is an instance (rather than a class) you would still have the possibility to avoid the explicitly encouraged restriction of the single instance and be able to create another one. This would violate your own rules, but it is always better to be able to do so. One simple case for wanting to do this is testing. You could create a second instance which connects to say, another version of the library, and test it without having to modify the class. The other reason for not choosing class-side methods is more subtle: classes represent the concept of the thing, not the thing. Consequently, their natural protocol is different from the protocol of their instances. Having the separation of both interfaces will add clarity to your design.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With