I have a resource (e.g., current program configuration) that I want to handle in a single class instance, but want to hide the fact that there is, in effect, a singleton class by using a proxy class.
Reasons for not using a singleton directly include: don't want users of the class to have to change code if I move away from singleton implementation, and want to be able to have control over initialization and destruction order (which would be difficult with static classes, for example).
Additionally, this is a multithreaded application, so new instances of the Proxy class may be created at the same time (thus the mutex protecting creation of the Implementation).
The Proxy
class is the proxy, and Impl
class is the hidden singleton implementation. Note that I don't do anything special to Impl
to make it a singleton. It is only a singleton because I track its existence in Proxy
with a static member variable.
Proxy.h
#pragma once
#include <memory>
class Proxy {
public:
Proxy();
void method1() //inline dispatch to hidden single class
{
data_->method1();
}
private:
class Impl { //this class will be created once only
public:
void method1();
Impl();
~Impl() = default;
};
std::shared_ptr<Impl> Make_Impl() const;
const std::shared_ptr<Impl> data_{};
inline static std::weak_ptr<Impl> impls_{}; //tracks whether Impl is created
};
Proxy.cpp
#include "Proxy.h"
#include <mutex>
Proxy::Proxy() : data_(Make_Impl()) {}
std::shared_ptr<Proxy::Impl> Proxy::Make_Impl() const
{ // note that impls_ may be accessed by another thread with non-const member function
// (assignment) if it is running slightly ahead of this thread with an uninitialized impls_ so
// need mutex before shared_ptr.lock call. if we use an atomic weak pointer (C++20) can call
// shared_ptr.lock before mutex, check for success, and only run mutex (and try lock again) after
// failure
static std::mutex mtx;
auto lock = std::lock_guard(mtx);
std::shared_ptr<Impl> internal_data = impls_.lock(); //if implementation exists, assign to shared_ptr
if (!internal_data) { //need to create implementation
const auto new_impl(std::make_shared<Impl>());
internal_data = new_impl;
impls_ = new_impl;
}
return internal_data;
}
Proxy::Impl::Impl()
{
// constructor for Impl here
}
void Proxy::Impl::method1()
{
// the real method1 for the implementation
}
General comments welcome. Specific questions include:
- is there a way to eliminate the pointer dereference that will occur with each call intended for the underlying implementation? (For example, When Proxy::method1 is called, that redirects to the underlying singleton class.)
- Instead of using a static weak_ptr, is there a better way of tracking whether the Implementation exists?
- Have others done anything like this? I'm concerned that I haven't seen this pattern (with smart pointers) documented anywhere. It makes me feel like I've gone "off the rails" with this code and have overlooked some problems with this approach.