In my effort to make a Swift version of SDL, I’m wondering if there’s a way to specify automatic module/library-level initialization and deinitialization? SDL has two APIs, SDL_Init(Uint32) and SDL_Quit(). SDL_Init() needs to be called before anything else can be used, and SDL_Quit() should be called upon program termination.
This isn’t particularly Swifty, so I’m wondering if there’s any mechanism for implicitly calling something when a module is loaded and unloaded.
Alternatively, I’ll be wrapping all of the “classes” in SDL in Swift types, and those could reference a singleton that handles the SDL init/deinit, but it’s a lot of boilerplate my code will have to include. Is there a facility in Swift that would make this easier?
[Just to set expectations here, I’m coming at this from the perspective of Apple platforms.]
With Mach-O, it’s possible to set up module initialisation and termination routines. See the discussion of -init in the ldman page. However, use of this construct is strongly discouraged because it forces everyone who imports the library to pay the cost for initialising it. Rather, we recommend lazy initialisation although, as you’ve noted, that’s not always super convenient.
My preferred way to approach this is to force the work on to my clients (-: by defining a class and forcing all access to go through that class. The class’s initialisation can then ensure that the underlying library is initialised. For example:
public class Library {
public init() {
… initialise underlying C library here …
}
public func makeWindow() -> Window {
…
}
}
public class Window {
// This not public, so the client has to go through `Library`
// to get a `Window`, and `Library` takes care of initialising
// the underlying C library.
fileprivate init() {
…
}
}
Perhaps the pragmatic thing is to ask clients of your wrapper to call SDL_Init and SDL_Quit (or wrappers to such) at the most appropriate times.
If you're trying to be clever, the reference-to-a-singleton approach might work. But if one object with such a reference stays alive (as part of a reference cycle for instance) it could prevent SDL_Quit from being called and the cause might be a bit hard to find. Not sure doing all this is better than having to call SDL_Quit manually.
(Also, if you intend this to run on iOS: there's probably no right time to call SDL_Quit there since iOS apps never really "quit".)
I agree, I’ll just let clients call it. And no, the intent is to run on Beaglebone Black or RPi, but also on macOS for development. I did this once before using C++ and it was a nice way to develop a specialized app. Would like to do the same from Swift.
A good development tool should not decide which mechanism is the best for some cases. It should be the decision of the developer, not the tool. So, the best development tool should provide as many as possible solutions for as many as possible use cases. Although in most cases module level init/deinit is discouraged, however it shouldn't be forbidden because it's very possible there are some scenarios that need such mechanism. Just provide the mechanism and inform the risk, but still let the developer decides and takes the risk.