C++ two phase initialization

Viewed 342

I'm writing a C++17 GUI library that uses two phase initialization. It sits on top of native GUI API (at present: Win32) and hides its complexity from the user.

Mapping Windows to C++ classes commonly requires two phase initialization: first phase creates C++ object and second phase creates API object.

This is required because the first opportunity to subscribe to window events is inside the constructor. Hence if your base constructor would already create window before you subscribe to events you would miss some. In addition to that one shouldn't call virtual functions from constructors in C++, so there's just no way around it.

I'd like to use the following pattern, to assure two phase initialization of a Windows resource (Window, GDI handles, etc.):

All functions that use resources have create() and destroy() member functions, derived from base template class.

template <class T>
class resource {
public:
    virtual T* create()=0;
    virtual void destroy()=0;
}

I can then derive classes from this:

class wnd : resource<wnd> {
    // do the magic
}

To create these classes I would like to create a global library function that does something like this:

template <class T, typename... A>
// TODO: Need a concept here, but not available yet.
static std::unique_ptr<T> create(A... args)
{
    T* ptr = new T(args...);
    ptr->create();
    return std::unique_ptr<T>(ptr);
}

So I'd call it like auto button=::create<button>(ctor args) and it would in turn pass arguments to ctor, return unique_ptr to my button and call create function on it.

Now I would like to implement destruction too. It would be attached to unique_ptr as custom deleter and would call destroy(). This is how I -in theory- think it should work.

// --- two phase construction pattern ---
template <typename T>
struct destroy {
    void operator()(T* p) { p->destroy(); delete p; }
};

template <class T, typename... A>
static std::unique_ptr<T> create(A... args)
{
    T* ptr = new T(args...);
    ptr->create();
    return std::unique_ptr<T, destroy<T>>(ptr);
}

The problem is that this fails because std::unique_ptr<T, destroy> is not convertible to std::unique_ptr<T,std::default_delete). Since a lot of functions accept std::unique_ptr derivates, I can't add another template parameter to all of them, it would complicate things for the library user.

I thought about calling destroy in destructors, but then I read that constructors and destructors should avoid calling virtual functions so I'd have to re-implement this in every destructor. Library user might forget this in derived class and create a memory leak.

Is there a nicer solution for this problem?

1 Answers

You could write a "holder" class that is responsible for creating the object, initializing it, finalizing it and destroying it.

#include <iostream>
#include <memory>

template<class base>
class holder {
public:
    template<class T, class... Args>
    static holder createAndInitialize(Args... args) {
      holder h = create<T, Args...>(args...);
      h.initialize();
      return h;
    }
    // to create but not initialize
    template<class T, class... Args>
    static holder create(Args... args) {
      return holder(new T(args...));
    }
    void initialize() {
      if (!m_initialized)
      {
        mres->initialize();
        m_initialized = true;
      }
    }
    void finalize()
    {
      if (m_initialized) {
        mres->finalize();
        m_initialized = false;
      }
    }
    // access to the underlying pointer
    const base *ptr() const {
        return mres.get();
    }
    base *ptr() {
        return mres.get();
    }
    ~holder() {
      finalize();
      std::cout << "holder destructor" << std::endl;
    }
private:
    holder(base * res)
      : mres(res), m_initialized(false)
      { }
    holder(holder &&rhs)
    : mres(std::move(rhs.mres)),
      m_initialized(rhs.m_initialized)
      { }
    std::unique_ptr<base> mres;
    bool m_initialized;
};

class resource {
private: // Make these private so that clients cannot invoke directly
    virtual void initialize()=0;
    virtual void finalize()=0;
    
 public:
    virtual ~resource() {}
    virtual void doSomething()=0;
    friend class holder<resource>;
};

class wnd : public resource {
    
public:
    void initialize() override {
        std::cout << "in wnd " << mv << " initialize" << std::endl;
    }
    void finalize() override {
        std::cout << "in wnd " << mv << " finalize" << std::endl;
    }
    void doSomething() override {
        std::cout << "in wnd " << mv << " doSomething" << std::endl;
    }
    ~wnd() {
        std::cout << "in wnd " << mv << " destructor" << std::endl;
    }
private:
    wnd(int v)// make constructor private
      : mv(v)
    {
        std::cout << "in wnd " << v << " constructor" << std::endl;
    }
    
    friend class holder<resource>; // add holder as friend
    int mv;
};

// class which has some child resources
class wnd2 : public resource {
    
public:
    // initialize the child objects
    void initialize() override {
        std::cout << "in wnd2 initialize" << std::endl;
        mres1.initialize();
        mres2.initialize();
    }
    // finalize the child objects
    void finalize() override {
        mres1.finalize();
        mres2.finalize();
        std::cout << "in wnd2 finalize" << std::endl;
    }
    void doSomething() override {
        std::cout << "in wnd2 doSomething" << std::endl;
    }
    ~wnd2() {
      std::cout << "wnd2 destructor" << std::endl;
    }
private:
    // In the constructor, the child objects are only created and not initialized
    wnd2()
      : mres1(holder<resource>::create<wnd>(1)),
        mres2(holder<resource>::create<wnd>(2))
      { }
      
    holder<resource> mres1;
    holder<resource> mres2;
    friend class holder<resource>; // add holder as friend
};

int main()
{
  holder<resource> h2 = holder<resource>::createAndInitialize<wnd2>();
  h2.ptr()->doSomething();
}
Related