Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* @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.
*/
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.
*/
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) */