Search code examples
c++c++11design-patternsunique-ptrdispose

unique_ptr as general purpose dispose object


Suppose I have a situation, where I have an SDK which provides certain interface, which implies some manual resources management or state changes. It is supposed to be used like this:

// SDK has state B by default
SDK->setStateA();
something();
requiring();
stateA();
SDK->setStateB();

Is it a good idea to incapsulate state changes as custom allocator/deleter for unique_ptr object or, probably, it would be better to get this behaviour through some manual Dispose pattern implementation.

Since it's not a resource allocation, I have doubts. It might cause confusion and make code cryptic.

My other concern is that I need a return code from both init and clean up steps. I could use lambdas and get those through captures, but it looks even more cryptic.

Maybe someone tried it already and saw how it makes code look after a while?


Solution

  • Generally, it is best to design the interface of the code in such a way that it is easy to use and intuitive, or, put differently, that it's hard to use it wrongly. In particular, if the interface is able to prevent bugs by refusing compilation, it can save a lot of debugging time.

    One possibility to achieve such an interface would be something that can be loosely related to the std::mutex <---> std::unique_lock mechanics:

    class state_guard {
       std::unique_ptr<SDK_type>& SDK;
    
       state_guard(std::unique_ptr<SDK_type>& s) : SDK{ s } {
           SDK->setStateA();
       }
    
       ~state_guard() {
           SDK->setStateB();
       }
    };
    
    void something(state_guard&, ...);
    void requiring(state_guard&, ...);
    void stateA(state_guard&, ...);
    
    std::unique_ptr<SDK_type> SDK{ get_sdk() };
    {
       state_guard guard{ SDK };
       something(guard, ...);
       requiring(guard, ...);
       stateA(guard, ...);
    }
    

    By forcing to pass the guard as a function argument (even if it is unused in the respective function), the user cannot forget to set to stateA (and thanks to RAII, to reset it to stateB).