I am building a c++ library and I would need to initialize an openGL context for my program ( and other default resource objects ). Would it be good practice ( considering modern c++ semantics ) to "hide the Init. code" in a code chunk being called before main ? I cannot use static variables because I need to initialize things in a specific order ! (I cannot init a texture before openGLm or SDL !)
Here is my code :
#include <stdio.h>
#ifdef _MSC_VER // For msvc / msvc++ 2015
#define CCALL __cdecl
#pragma section(".CRT$XCU",read)
#define INITIALIZER(f) \
static void __cdecl f(void); \
__declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \
static void __cdecl f(void)
#elif defined(__GNUC__) for gcc / g++
#define CCALL
#define INITIALIZER(f) \
static void f(void) __attribute__((constructor)); \
static void f(void)
#endif
static void CCALL finalize(void)
{
/* Some king of delete / dispose here : like SDL_Quit, ... */
}
INITIALIZER(initialize)
{
/*
HERE IS THE STARTUP CODE ...
*/
}
The standard, section 3.6.2, says that non-local static variables may be initialized before the first statement of main
:
It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main.
Thus, it doesn't say they must be initialized before the first statement of main
. There is no way in C++ to enforce a constraint that anything would be initialized before main()
is called, except for static variables defined in the same compilation unit as main.
The best solution is simply to include your resource management within main
itself:
int main()
{
// Load resources (housekeeping code)
// Do real work (applicative code)
// Release resources (housekeeping code)
}
If you want to separate the applicative code from the housekeeping code, a commonly used approach is to use inversion of control (sometimes via a template method pattern):
main
is responsible both for doing the housekeeping and for invoking the user-code or applicative-code function or method.This is exemplified below:
// The function below is expected to exist by the framework
// It must be implemented by the user
int userMain()
{
// Implement user-code here
}
// The code below is implemented by the framework
// It expects the user to have implemented a function userMain
int main()
{
FrameWorkData theFrameWorkData;
// The framework performs its initialization house-keeping here
theFrameWorkData.initialize();
// The framework invokes the user-code (inversion of control)
userMain();
// The framework performs its clean-up house-keeping here
theFrameWorkData.cleanup();
}