HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
staticData.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_STATIC_DATA_H
8 #define PXR_BASE_TF_STATIC_DATA_H
9 
10 /// \file tf/staticData.h
11 /// \ingroup group_tf_Initialization
12 
13 #include "pxr/pxr.h"
14 #include "pxr/base/arch/hints.h"
16 
17 #include <atomic>
18 #include <type_traits>
19 
21 
22 /// \class TfStaticData
23 /// \ingroup group_tf_Initialization
24 ///
25 /// Create or return a previously created object instance of global data.
26 ///
27 /// Any form of global data that requires an constructor (even a default
28 /// constructor) is unsafe to declare as global data. By global data we mean
29 /// either a variable defined at file-scope (outside of a function) or a
30 /// static member of a class. This is because the initialization order of
31 /// globals is undefined across translation units.
32 ///
33 /// The only exceptions are constexpr constructors and "plain old data" types
34 /// such as integral or float/double type and pointers. In contrast, \c
35 /// std::string requires construction, as do most \c STL types, and most
36 /// user-defined types as well. Note that static local variables in functions
37 /// are also safe and are initialized in a thread-safe manner the first time
38 /// they're encountered.
39 ///
40 /// One way to handle this problem is to go the singleton route, which can be
41 /// done using the \c TfSingleton pattern. However, a fair amount of coding is
42 /// required for this, and at times, something more lightweight is appropriate.
43 /// For these few cases, the following construct may be employed:
44 ///
45 /// \code
46 /// // source file:
47 ///
48 /// #include <set>
49 /// #include <string>
50 ///
51 /// static TfStaticData<set<string> > Xyz_nameSet;
52 ///
53 /// void XyzAddName(string name) {
54 /// Xyz_nameSet->insert(name);
55 ///
56 /// ...
57 /// }
58 /// \endcode
59 ///
60 /// One uses a \c TfStaticData<T> as if it were a pointer; upon first use
61 /// however, the item is initialized to point at a new object of type \c T. Note
62 /// that the type \c T must have a default constructor; that is, the newly
63 /// created object is created by calling \c "new T".
64 ///
65 /// If you have no need to access the data, but need to make sure it has been
66 /// initialized (for example, if the type's constructor will have some effect
67 /// that you need to be sure has happened), you can call the Touch() method.
68 ///
69 /// Warning: the \c TfStaticData construct relies upon zero-initialization of
70 /// global data: therefore, you can only use this structure for static data
71 /// member of classes or variables declare at file-scope. Do \e not declare
72 /// a \c TfStaticData object as a local variable, as a member of a class or
73 /// structure, or as a function parameter. Use normal static local variable
74 /// initialization inside a function.
75 ///
76 /// One can either call member functions using the "->" operator, or use the
77 /// dereference "*" operator:
78 ///
79 /// \code
80 /// TfStaticData<string> Xyz_curName;
81 ///
82 /// void Xyz_SetLastName(string s) {
83 /// *Xyz_curName = s;
84 ///
85 /// vector<string> v;
86 /// v.push_back(*Xyz_curName);
87 /// }
88 /// \endcode
89 ///
90 template <class T>
92  static T *New() { return new T; }
93 };
94 
95 template <class T, class Factory = Tf_StaticDataDefaultFactory<T> >
96 class TfStaticData {
97 public:
98  /// Return a pointer to the underlying data object. It is created and
99  /// initialized if necessary.
100  inline T* operator-> () const { return Get(); }
101 
102  /// Member lookup. The underlying data object is created and initialized
103  /// if necessary.
104  inline T& operator* () const { return *Get(); }
105 
106  /// Return a pointer to the underlying object, creating and initializing
107  /// it if necessary.
108  inline T* Get() const {
109  T *p = _data;
110  return ARCH_LIKELY(p) ? p : _TryToCreateData();
111  }
112 
113  /// Return true if the underlying data object is created and initialized.
114  /// Return false otherwise.
115  inline bool IsInitialized() const { return _data.load() != nullptr; }
116 
117 private:
118  T *_TryToCreateData() const {
119  // Allocate an instance.
120  T *tmp = Factory::New();
121 
122  // Try to atomically set the pointer from null to tmp.
123  T *n = nullptr;
124  if (ARCH_LIKELY(_data.compare_exchange_strong(n, tmp)))
125  return tmp;
126 
127  // Another thread won the initialization race.
128  delete tmp;
129  return _data;
130  }
131 
132  mutable std::atomic<T *> _data;
133 };
134 
135 /// Create a static data object, initializing it with code.
136 ///
137 /// The macro takes two arguments. The first is the type of static data, the
138 /// second is the name of the variable. The block of code following the macro
139 /// will be invoked to initialize the static data when it is first used. See
140 /// example usage:
141 ///
142 /// \code
143 /// TF_MAKE_STATIC_DATA(string, myString) { *myString = "hello!"; }
144 ///
145 /// TF_MAKE_STATIC_DATA(vector<string>, someNames) {
146 /// someNames->push_back("hello");
147 /// someNames->push_back("static");
148 /// someNames->push_back("world");
149 /// }
150 ///
151 /// TF_MAKE_STATIC_DATA((map<int, int>), intMap) {
152 /// (*intMap)[1] = 11;
153 /// (*intMap)[2] = 22;
154 /// }
155 /// \endcode
156 ///
157 /// If the type uses commas to separate template arguments you need to enclose
158 /// the type in parentheses as shown in the last example.
159 ///
160 /// If the data does not need to be mutated after initialization, you may
161 /// specify a const type. The underlying data is non-const but the
162 /// TfStaticData accessors only provide const access.
163 ///
164 /// \code
165 /// TF_MAKE_STATIC_DATA(const vector<string>, constNames) {
166 /// constNames->push_back("hello");
167 /// constNames->push_back("static const");
168 /// constNames->push_back("world");
169 /// }
170 /// \endcode
171 ///
172 /// Note that this macro may only be used at namespace scope (not function
173 /// scope).
174 ///
175 /// Also note that in multithreaded code, it is possible for the provided code
176 /// to be invoked more than once (with different target objects) for the same
177 /// static data instance. This is fine as long as the initialization code does
178 /// not have side-effects, but you should be aware of it.
179 ///
180 /// \hideinitializer
181 #define TF_MAKE_STATIC_DATA(Type, Name) \
182  static void TF_PP_CAT(Name,_Tf_StaticDataFactoryImpl)( \
183  std::remove_const_t<TF_PP_EAT_PARENS(Type)> *); \
184  namespace { \
185  struct TF_PP_CAT(Name,_Tf_StaticDataFactory) { \
186  static TF_PP_EAT_PARENS(Type) *New() { \
187  auto *p = new std::remove_const_t<TF_PP_EAT_PARENS(Type)>; \
188  TF_PP_CAT(Name,_Tf_StaticDataFactoryImpl)(p); \
189  return p; \
190  } \
191  }; \
192  } \
193  static TfStaticData< \
194  TF_PP_EAT_PARENS(Type), TF_PP_CAT(Name,_Tf_StaticDataFactory)> Name; \
195  static void TF_PP_CAT(Name,_Tf_StaticDataFactoryImpl)( \
196  std::remove_const_t<TF_PP_EAT_PARENS(Type)> *Name)
197 
199 
200 #endif
#define ARCH_LIKELY(x)
Definition: hints.h:29
T & operator*() const
Definition: staticData.h:104
GLdouble n
Definition: glcorearb.h:2008
T * operator->() const
Definition: staticData.h:100
bool IsInitialized() const
Definition: staticData.h:115
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
T * Get() const
Definition: staticData.h:108
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74