I am using a 3rd party library that must be initialized with Lib::init()
before any of its other functions may be called and that must be uninitialized with Lib::destroy()
before the application ends. My classes that use the library just assume that it is initialized. This is not a problem in my application - I am calling init()
immediately when entering main
and I am calling destroy()
directly before leaving it.
Unfortunately, in my unit tests, I sometimes forget to initialize the library. This made me thinking: If I forget to call Lib::init()
, other users of my classes will also forget to call it. To tackle this problem, I added a RAII wrapper that calls Lib::init()
in the constructor and Lib::destroy()
in the destructor:
struct LibInstance
{
LibInstance()
{
Lib::init();
}
LibInstance(LibInstance const&) = delete;
LibInstance(LibInstance&&) = delete;
LibInstance& operator=(LibInstance const&) = delete;
LibInstance& operator=(LibInstance&&) = delete;
~LibInstance()
{
Lib::destroy();
}
};
Now all of my classes that use the library require a LibInstance const&
argument in the constructor or in the respective member function. This way, it is guaranteed that init()
has been called before:
struct MyClass
{
int do_something_with_lib(LibInstance const& /*instance*/)
{
// Here, we can be sure that Lib::init() has been called,
// because the user was able to pass a LibInstance argument.
Lib::foo();
return Lib::bar();
}
};
At first, this wrapper seemed like a really good solution and I was happy about it. It is impossible to use my class without initializing the library. However, after some time, I find it really strange that many of my classes now require a LibInstance
argument and never access it. Additionally, it is sometimes inconvenient to pass the LibInstance
from main
down to the place where it is actually used.
Is it a good idea that my classes take an additional (unused) argument just for ensuring that Lib::init()
has been called?
If it is not a good idea, what is a better way to ensure the initialization?
Lib
have a way to see ifinit
has been called? Is there any of your classes that is always used, or first used?Lib::is_initialized()
(or something comparable). Of course, I could wrap the function and set a globalbool lib_is_initialized
, but I want to avoid globals if possible. Regarding your second question: In my main application, there are classes that are always used (e.g.,Engine
with a main loop). However, these classes are (currently) not necessary in the unit tests and most components can be tested on their own.LibInstance
argument makes it nearly impossible to forget the initialization. I can also live with the fact that this requires passing the instance to all necessary places. However, it feels like I am implementing some kind of anti pattern, even though I can say exactly why I feel that way.LibInstance
be a wrapper for the whole library, rather than just theinit
/destroy
calls? That way, you wouldn't have unused arguments, because the argument would be used to reach the library functions.LibInstance::foo()
instead ofLib::foo()
? That would give more meaning to theLibInstance
arguments. It is probably not feasible for large libraries, but it might work in my case.