This is less true than you really think it is: even APIs that are published “as C” [or C compatible] are often hiding something that is not C. Take, for example, OpenGL, what it describes is an interface that abstracts over a state-machine (or is it finite automata? it’s been a while since I touched OpenGL) where the correct usage of the API is contained with certain calls occurring only between glBegin
and glEnd
— such an interface could be achieved in Ada with two types one representing the device, one representing the scene which has the manipulation operations (the things between glBegin
and glEnd
); where the second type is put into an operation of the first type whic does the glBegin
and glEnd
appropriately.
I bring up OpenGL specifically because despite the “common knowledge” that OpenGL is C, it is not, and was designed to be used by multiple languages — it is regrettable that the documentation/definition is in C-ish syntax, misleading people into that conclusion.
There are reasons where you might want to do a binding to C, even if your “end goal” is C-free.
Consider, for a moment, the Andor camera API; it is currently set up as a DLL with C entries. You could make a system that is end-to-end Ada, bare metal… but doing so means that you forego the benefits of having an OS: memory management, files, etc and would have to implement your own. — You would have to implement the USB protocol, and management thereof and devices, you would have to implement the DB9 com-port [IIRC], and you would have to implement the specialized connecter some of the cameras have, then you would have to implement the camera-control protocol, and over each of them. (You could do this as generic
though, so not quite as bad as it may seem.)
But, sometimes you can’t be afforded the time to [essentially] clean-room implement —say that your org needs to keep producing data with the cameras to get funding— so then, you do it incrementally:
- First import the API into Ada, ultra-thin binding.
- Then use types/subtypes to restrict the return values to the API-spec allowed.
(e.g. Post => Fn'Result in API_SUCCESS | API_FAILURE | API_UNINITALIZED
.)
- Then “hoist” the above constraints into more appropriate types:
(e.g. Function Fn return Boolean; -- May raise UNINITALIZED.
.)
- [Optional] Implement a package implementing the actual model in the API, like the above OpenGL example, but with Andor’s cameras.
- Replace SW using the old API with SW using this superthick binding, if you did that; or else rewrite/refactor the old code so that you’re simplifying it, allowing the now Ada API to handle things like the error return-values. (i.e. if you export your Ada subprograms, with Convention C, the errors that pre/post/subtypes catch can have their handlers removed from the client C-code, cleaning & simplifying that.)
And, depending on how far you go, you can remove more dependence on C until there is none, perhaps delving into direct HW control (or direct camera-protocol pushed via USB) rather than using the DLLs/drivers from Andor.
Ada’s easy foreign function interfacing makes it possible to do this with minimal disruption, and in a [generally] cost-effective manner.
This really depends on the situation; if it’s modernizing/maintenance of old tech/SW, you may have to do it as above just to keep the managerial caste happy. If it’s a new project, 100% Ada might be possible… or you might have constraints with “all the pieces” being C-ish APIs like above, with a tight schedule, in which case points 1 through 3 will give you a HUGE advantage (because you’re removing error-states and moving detection and some handling to the point-of-origin, or close to it).
But perhaps the best way to handle in that situation would be to do a good model of the problem-space/model abstracted by the API, then do the above, so that you can (if needed) swap out the C-API/bindings transparently if you need to. (Remember Ada has excellent low-level control.)