0

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?

6
  • Does Lib have a way to see if init has been called? Is there any of your classes that is always used, or first used? Commented Feb 22, 2020 at 19:31
  • Unfortunately there is no Lib::is_initialized() (or something comparable). Of course, I could wrap the function and set a global bool 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.
    – pschill
    Commented Feb 22, 2020 at 20:02
  • I really like that the additional 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.
    – pschill
    Commented Feb 22, 2020 at 20:16
  • Is it feasible to let LibInstance be a wrapper for the whole library, rather than just the init/destroy calls? That way, you wouldn't have unused arguments, because the argument would be used to reach the library functions. Commented Feb 24, 2020 at 9:08
  • @BartvanIngenSchenau Do you mean that I wrap all library calls and then use LibInstance::foo() instead of Lib::foo()? That would give more meaning to the LibInstance arguments. It is probably not feasible for large libraries, but it might work in my case.
    – pschill
    Commented Feb 24, 2020 at 11:32

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.