Erstens wollen Sie einige Thread-Sicherheit:
template<class T, class M=std::shared_timed_mutex> // shared_mutex in C++17
struct mutex_guarded {
template<class F>
auto write(F&& f)
->std::decay_t<std::result_of_t<F(T&)>> {
auto l = lock();
return std::forward<F>(f)(t);
}
template<class F>
auto read(F&& f) const
->std::decay_t<std::result_of_t<F(T const&)>> {
auto l = lock();
return std::forward<F>(f)(t);
}
mutex_guarded() {}
template<class T0, class...Ts,
std::enable_if_t<!std::is_same<std::decay_t<T0>, mutex_guarded>{},int> =0
>
mutex_guarded(T0&& t0, Ts&&...ts):
t(std::forward<T0>(t0), std::forward<Ts>(ts)...)
{}
mutex_guarded(mutex_guarded const& o):
t(o.copy_from())
{}
mutex_guarded(mutex_guarded && o):
t(o.move_from())
{}
mutex_guarded& operator=(mutex_guarded const&)=delete;
mutex_guarded& operator=(mutex_guarded &&)=delete;
mutex_guarded& operator=(T const& t) {
write([&t](T& dest){dest=t;});
return *this;
}
mutex_guarded& operator=(T&& t) {
write([&t](T& dest){dest=std::move(t);});
return *this;
}
private:
T copy_from() const& { return read([](T const& t){ return t; }); }
T copy_from() && { return move_from(); }
T move_from() { return write([](T& t){ return std::move(t); }); }
std::unique_lock<M> lock() const {
return std::unique_lock<M>(m);
}
std::shared_lock<M> lock() {
return std::shared_lock<M>(m);
}
M m; // mutex
T t;
};
, die uns ein lets have:
using foo_factory = std::function<std::unique_ptr<Foo>()>;
using foo_factories = std::unordered_map<std::string, foo_factory>;
mutex_guarded<foo_factories>& get_foo_factories() {
static mutex_guarded<foo_factories> map;
return map;
}
die Thread-sichere Initialisierung hat, dann
void registerFoo(std::string name, std::function<Foo *()> factory)
{
get_foo_factories().write([](auto& f){f.emplace(name, factory);});
}
ist Thread-sicher und garantiert früh genug die Initialisierung der Fabriken.
Bei der Abschaltung ist das Timing der Zerstörung der Fabriken sowohl außerhalb Ihrer Kontrolle (umgekehrte Reihenfolge der Konstruktion) und möglicherweise zu früh.
mutex_guarded<foo_factories>*& get_foo_factories() {
static auto* map = new mutex_guarded<foo_factories>;
return map;
}
void registerFoo(std::string name, std::function<Foo *()> factory)
{
get_foo_factories()->write([](auto& f){f.emplace(name, factory);});
}
void end_foo_factories() {
auto*& ptr = get_foo_factories();
delete ptr; ptr = nullptr;
}
Dies wird es auf den Haufen legen, wo es ein bisschen länger leben wird. Beachten Sie, dass dadurch auch die Fabriken und die Karte verloren gehen. manuelle Zerstörung "spät genug" kann hinzugefügt werden. Beachten Sie, dass diese Zerstörung nicht Thread-sicher ist, noch kann es billig threadsicher gemacht werden; Es sollte auftreten, nachdem alle Threads bereinigt wurden.
Das ist was ich tue. Ich mache auch manchmal einen 'boost ::' oder 'std :: optional &', der standardmäßig mit einem 'blah' konstruiert wird, weil das mir erlaubt, solche Singletons in der von mir gewählten Reihenfolge zu zerstören, anstatt in Compiler- bestimmte pro-Laufzeit umgekehrte Reihenfolge der Konstruktion. –
Yakk
Ich beachte, dass die obige Lösung von VTT ein dynamisch zugewiesenes Objekt verwendet. Hat das irgendeinen Vorteil? – Nuoji
Die typische Rechtfertigung für die Verwendung von dynamischer Zuordnung oder ähnlichem Hacking besteht darin, das Objekt während der Zerstörung globaler Objekte verfügbar zu machen. Kann oder kann kein tatsächliches Problem sein, abhängig vom Anwendungsfall. –