/** * @file llinitdestroyclass.h * @author Nat Goodspeed * @date 2015-05-27 * @brief LLInitClass / LLDestroyClass mechanism * * The LLInitClass template, extracted from llui.h, ensures that control will * reach a static initClass() method. LLDestroyClass does the same for a * static destroyClass() method. * * The distinguishing characteristics of these templates are: * * - All LLInitClass<T>::initClass() methods are triggered by an explicit call * to LLInitClassList::instance().fireCallbacks(). Presumably this call * happens sometime after all static objects in the program have been * initialized. In other words, each LLInitClass<T>::initClass() method * should be able to make some assumptions about global program state. * * - Similarly, LLDestroyClass<T>::destroyClass() methods are triggered by * LLDestroyClassList::instance().fireCallbacks(). Again, presumably this * happens at a well-defined moment in the program's shutdown sequence. * * - The initClass() calls happen in an unspecified sequence. You may not rely * on the relative ordering of LLInitClass<T>::initClass() versus another * LLInitClass<U>::initClass() method. If you need such a guarantee, use * LLSingleton instead and make the dependency explicit. * * - Similarly, LLDestroyClass<T>::destroyClass() may happen either before or * after LLDestroyClass<U>::destroyClass(). You cannot rely on that order. * * $LicenseInfo:firstyear=2015&license=viewerlgpl$ * Copyright (c) 2015, Linden Research, Inc. * $/LicenseInfo$ */ #if ! defined(LL_LLINITDESTROYCLASS_H) #define LL_LLINITDESTROYCLASS_H #include "llsingleton.h" #include <boost/function.hpp> #include <typeinfo> #include <vector> #include <utility> // std::pair /** * LLCallbackRegistry is an implementation detail base class for * LLInitClassList and LLDestroyClassList. It accumulates the initClass() or * destroyClass() callbacks for registered classes. */ class LLCallbackRegistry { public: typedef boost::function<void()> func_t; void registerCallback(const std::string& name, const func_t& func) { mCallbacks.push_back(FuncList::value_type(name, func)); } void fireCallbacks() const; private: // Arguably this should be a boost::signals2::signal, which is, after all, // a sequence of callables. We manage it by hand so we can log a name for // each registered function we call. typedef std::vector< std::pair<std::string, func_t> > FuncList; FuncList mCallbacks; }; /** * LLInitClassList is the LLCallbackRegistry for LLInitClass. It stores the * registered initClass() methods. It must be an LLSingleton because * LLInitClass registers its initClass() method at static construction time * (before main()), requiring LLInitClassList to be fully constructed on * demand regardless of module initialization order. */ class LLInitClassList : public LLCallbackRegistry, public LLSingleton<LLInitClassList> { LLSINGLETON_EMPTY_CTOR(LLInitClassList); }; /** * LLDestroyClassList is the LLCallbackRegistry for LLDestroyClass. It stores * the registered destroyClass() methods. It must be an LLSingleton because * LLDestroyClass registers its destroyClass() method at static construction * time (before main()), requiring LLDestroyClassList to be fully constructed * on demand regardless of module initialization order. */ class LLDestroyClassList : public LLCallbackRegistry, public LLSingleton<LLDestroyClassList> { LLSINGLETON_EMPTY_CTOR(LLDestroyClassList); }; /** * LLRegisterWith is an implementation detail for LLInitClass and * LLDestroyClass. It is intended to be used as a static class member whose * constructor registers the specified callback with the LLMumbleClassList * singleton registry specified as the template argument. */ template<typename T> class LLRegisterWith { public: LLRegisterWith(const std::string& name, const LLCallbackRegistry::func_t& func) { T::instance().registerCallback(name, func); } // this avoids a MSVC bug where non-referenced static members are "optimized" away // even if their constructors have side effects S32 reference() { S32 dummy; dummy = 0; return dummy; } }; /** * Derive MyClass from LLInitClass<MyClass> (the Curiously Recurring Template * Pattern) to ensure that the static method MyClass::initClass() will be * called (along with all other LLInitClass<T> subclass initClass() methods) * when someone calls LLInitClassList::instance().fireCallbacks(). This gives * the application specific control over the timing of all such * initializations, without having to insert calls for every such class into * generic application code. */ template<typename T> class LLInitClass { public: LLInitClass() { sRegister.reference(); } // When this static member is initialized, the subclass initClass() method // is registered on LLInitClassList. See sRegister definition below. static LLRegisterWith<LLInitClassList> sRegister; }; /** * Derive MyClass from LLDestroyClass<MyClass> (the Curiously Recurring * Template Pattern) to ensure that the static method MyClass::destroyClass() * will be called (along with other LLDestroyClass<T> subclass destroyClass() * methods) when someone calls LLDestroyClassList::instance().fireCallbacks(). * This gives the application specific control over the timing of all such * cleanup calls, without having to insert calls for every such class into * generic application code. */ template<typename T> class LLDestroyClass { public: LLDestroyClass() { sRegister.reference(); } // When this static member is initialized, the subclass destroyClass() // method is registered on LLInitClassList. See sRegister definition // below. static LLRegisterWith<LLDestroyClassList> sRegister; }; // Here's where LLInitClass<T> specifies the subclass initClass() method. template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(std::string(typeid(T).name()) + "::initClass", &T::initClass); // Here's where LLDestroyClass<T> specifies the subclass destroyClass() method. template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(std::string(typeid(T).name()) + "::destroyClass", &T::destroyClass); #endif /* ! defined(LL_LLINITDESTROYCLASS_H) */