176 lines
6.1 KiB
C++
176 lines
6.1 KiB
C++
/**
|
|
* @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) */
|