class SingletonVault {
public:
- SingletonVault() {};
+ enum class Type { Strict, Relaxed };
+
+ explicit SingletonVault(Type type = Type::Relaxed) : type_(type) {}
~SingletonVault();
typedef std::function<void(void*)> TeardownFunc;
TeardownFunc teardown) {
std::lock_guard<std::mutex> guard(mutex_);
- CHECK_THROW(state_ == SingletonVaultState::Registering, std::logic_error);
+ stateCheck(SingletonVaultState::Registering);
CHECK_THROW(singletons_.find(type) == singletons_.end(), std::logic_error);
auto& entry = singletons_[type];
if (!entry) {
// registered at this point.
void registrationComplete() {
std::lock_guard<std::mutex> guard(mutex_);
- CHECK_THROW(state_ == SingletonVaultState::Registering, std::logic_error);
+ stateCheck(SingletonVaultState::Registering);
state_ = SingletonVaultState::Running;
}
Living,
};
+ void stateCheck(SingletonVaultState expected,
+ const char* msg="Unexpected singleton state change") {
+ if (type_ == Type::Strict && expected != state_) {
+ throw std::logic_error(msg);
+ }
+ }
+
// An actual instance of a singleton, tracking the instance itself,
// its state as described above, and the create and teardown
// functions.
SingletonEntry* get_entry(detail::TypeDescriptor type,
std::unique_lock<std::mutex>* lock) {
// mutex must be held when calling this function
- if (state_ != SingletonVaultState::Running) {
- throw std::logic_error(
- "Attempt to load a singleton before "
- "SingletonVault::registrationComplete was called (hint: you probably "
- "didn't call initFacebook)");
- }
+ stateCheck(
+ SingletonVaultState::Running,
+ "Attempt to load a singleton before "
+ "SingletonVault::registrationComplete was called (hint: you probably "
+ "didn't call initFacebook)");
auto it = singletons_.find(type);
if (it == singletons_.end()) {
detail::TypeDescriptorHasher> singletons_;
std::vector<detail::TypeDescriptor> creation_order_;
SingletonVaultState state_ = SingletonVaultState::Registering;
+ Type type_ = Type::Relaxed;
};
// This is the wrapper class that most users actually interact with.
// Some pathological cases such as getting unregistered singletons,
// double registration, etc.
TEST(Singleton, NaughtyUsage) {
- SingletonVault vault;
+ SingletonVault vault(SingletonVault::Type::Strict);
vault.registrationComplete();
// Unregistered.
}(),
std::logic_error);
- EXPECT_THROW([]() { Singleton<Watchdog> watchdog_singleton; }(),
- std::logic_error);
+ // Default vault is non-strict; this should work.
+ Singleton<Watchdog> global_watchdog_singleton;
- SingletonVault vault_2;
+ SingletonVault vault_2(SingletonVault::Type::Strict);
EXPECT_THROW(Singleton<Watchdog>::get(&vault_2), std::logic_error);
Singleton<Watchdog> watchdog_singleton(nullptr, nullptr, &vault_2);
// double registration