1 | /* This file is part of the KDE libraries |
2 | Copyright (C) 2007 Matthias Kretz <kretz@kde.org> |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) version 3, or any |
8 | later version accepted by the membership of KDE e.V. (or its |
9 | successor approved by the membership of KDE e.V.), Nokia Corporation |
10 | (or its successors, if any) and the KDE Free Qt Foundation, which shall |
11 | act as a proxy defined in Section 6 of version 3 of the license. |
12 | |
13 | This library is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | Lesser General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU Lesser General Public |
19 | License along with this library. If not, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | #ifndef PHONON_GLOBALSTATIC_P_H |
23 | #define PHONON_GLOBALSTATIC_P_H |
24 | |
25 | #include <QAtomicPointer> |
26 | |
27 | // |
28 | // WARNING!! |
29 | // This code uses undocumented Qt API |
30 | // Do not copy it to your application! Use only the functions that are here! |
31 | // Otherwise, it could break when a new version of Qt ships. |
32 | // |
33 | |
34 | namespace Phonon |
35 | { |
36 | |
37 | /** |
38 | * @internal |
39 | */ |
40 | typedef void (*CleanUpFunction)(); |
41 | |
42 | /** |
43 | * @internal |
44 | * |
45 | * Helper class for PHONON_GLOBAL_STATIC to clean up the object on library unload or application |
46 | * shutdown. |
47 | */ |
48 | class CleanUpGlobalStatic |
49 | { |
50 | public: |
51 | CleanUpFunction func; |
52 | |
53 | inline ~CleanUpGlobalStatic() { func(); } |
54 | }; |
55 | |
56 | } // namespace Phonon |
57 | |
58 | #ifdef Q_CC_MSVC |
59 | /** |
60 | * @internal |
61 | * |
62 | * MSVC seems to give anonymous structs the same name which fails at link time. So instead we name |
63 | * the struct and hope that by adding the line number to the name it's unique enough to never clash. |
64 | */ |
65 | # define PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) _k_##NAME##__LINE__ |
66 | #else |
67 | /** |
68 | * @internal |
69 | * |
70 | * Make the struct of the PHONON_GLOBAL_STATIC anonymous. |
71 | */ |
72 | # define PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) |
73 | #endif |
74 | |
75 | /** |
76 | * This macro makes it easy to use non-POD types as global statistics. |
77 | * The object is created on first use and creation is threadsafe. |
78 | * |
79 | * The object is destructed on library unload or application exit. |
80 | * Be careful with calling other objects in the destructor of the class |
81 | * as you have to be sure that they (or objects they depend on) are not already destructed. |
82 | * |
83 | * @param TYPE The type of the global static object. Do not add a *. |
84 | * @param NAME The name of the function to get a pointer to the global static object. |
85 | * |
86 | * If you have code that might be called after the global object has been destroyed you can check |
87 | * for that using the isDestroyed() function. |
88 | * |
89 | * If needed (If the destructor of the global object calls other functions that depend on other |
90 | * global statistics (e.g. KConfig::sync) your destructor has to be called before those global statistics |
91 | * are destroyed. A Qt post routine does that.) you can also install a post routine (@ref qAddPostRoutine) to clean up the object |
92 | * using the destroy() method. If you registered a post routine and the object is destroyed because |
93 | * of a lib unload you have to call qRemovePostRoutine! |
94 | * |
95 | * Example: |
96 | * @code |
97 | * class A { |
98 | * public: |
99 | * ~A(); |
100 | * ... |
101 | * }; |
102 | * |
103 | * PHONON_GLOBAL_STATIC(A, globalA) |
104 | * // The above creates a new globally static variable named 'globalA' which you |
105 | * // can use as a pointer to an instance of A. |
106 | * |
107 | * void doSomething() |
108 | * { |
109 | * // The first time you access globalA a new instance of A will be created automatically. |
110 | * A *a = globalA; |
111 | * ... |
112 | * } |
113 | * |
114 | * void doSomethingElse() |
115 | * { |
116 | * if (globalA.isDestroyed()) { |
117 | * return; |
118 | * } |
119 | * A *a = globalA; |
120 | * ... |
121 | * } |
122 | * |
123 | * void installPostRoutine() |
124 | * { |
125 | * // A post routine can be used to delete the object when QCoreApplication destructs, |
126 | * // not adding such a post routine will delete the object normally at program unload |
127 | * qAddPostRoutine(globalA.destroy); |
128 | * } |
129 | * |
130 | * A::~A() |
131 | * { |
132 | * // When you install a post routine you have to remove the post routine from the destructor of |
133 | * // the class used as global static! |
134 | * qRemovePostRoutine(globalA.destroy); |
135 | * } |
136 | * @endcode |
137 | * |
138 | * A common case for the need of deletion on lib unload/app shutdown are Singleton classes. Here's |
139 | * an example how to do it: |
140 | * @code |
141 | * class MySingletonPrivate; |
142 | * class EXPORT_MACRO MySingleton |
143 | * { |
144 | * friend class MySingletonPrivate; |
145 | * public: |
146 | * static MySingleton *self(); |
147 | * QString someFunction(); |
148 | * |
149 | * private: |
150 | * MySingleton(); |
151 | * ~MySingleton(); |
152 | * }; |
153 | * @endcode |
154 | * in the .cpp file: |
155 | * @code |
156 | * // This class will be instantiated and referenced as a singleton in this example |
157 | * class MySingletonPrivate |
158 | * { |
159 | * public: |
160 | * QString foo; |
161 | * MySingleton instance; |
162 | * }; |
163 | * |
164 | * PHONON_GLOBAL_STATIC(MySingletonPrivate, mySingletonPrivate) |
165 | * |
166 | * MySingleton *MySingleton::self() |
167 | * { |
168 | * // returns the singleton; automatically creates a new instance if that has not happened yet. |
169 | * return &mySingletonPrivate->instance; |
170 | * } |
171 | * QString MySingleton::someFunction() |
172 | * { |
173 | * // Refencing the singleton directly is possible for your convenience |
174 | * return mySingletonPrivate->foo; |
175 | * } |
176 | * @endcode |
177 | * |
178 | * Instead of the above you can use also the following pattern (ignore the name of the namespace): |
179 | * @code |
180 | * namespace MySingleton |
181 | * { |
182 | * EXPORT_MACRO QString someFunction(); |
183 | * } |
184 | * @endcode |
185 | * in the .cpp file: |
186 | * @code |
187 | * class MySingletonPrivate |
188 | * { |
189 | * public: |
190 | * QString foo; |
191 | * }; |
192 | * |
193 | * PHONON_GLOBAL_STATIC(MySingletonPrivate, mySingletonPrivate) |
194 | * |
195 | * QString MySingleton::someFunction() |
196 | * { |
197 | * return mySingletonPrivate->foo; |
198 | * } |
199 | * @endcode |
200 | * |
201 | * Now code that wants to call someFunction() doesn't have to do |
202 | * @code |
203 | * MySingleton::self()->someFunction(); |
204 | * @endcode |
205 | * anymore but instead: |
206 | * @code |
207 | * MySingleton::someFunction(); |
208 | * @endcode |
209 | * |
210 | * @ingroup KDEMacros |
211 | */ |
212 | #define PHONON_GLOBAL_STATIC(TYPE, NAME) PHONON_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ()) |
213 | |
214 | /** |
215 | * @overload |
216 | * This is the same as PHONON_GLOBAL_STATIC, but can take arguments that are passed |
217 | * to the object's constructor |
218 | * |
219 | * @param TYPE The type of the global static object. Do not add a *. |
220 | * @param NAME The name of the function to get a pointer to the global static object. |
221 | * @param ARGS the list of arguments, between brackets |
222 | * |
223 | * Example: |
224 | * @code |
225 | * class A |
226 | * { |
227 | * public: |
228 | * A(const char *s, int i); |
229 | * ... |
230 | * }; |
231 | * |
232 | * PHONON_GLOBAL_STATIC_WITH_ARG(A, globalA, ("foo", 0)) |
233 | * // The above creates a new globally static variable named 'globalA' which you |
234 | * // can use as a pointer to an instance of A. |
235 | * |
236 | * void doSomething() |
237 | * { |
238 | * // The first time you access globalA a new instance of A will be created automatically. |
239 | * A *a = globalA; |
240 | * ... |
241 | * } |
242 | * @endcode |
243 | * |
244 | * @ingroup KDEMacros |
245 | */ |
246 | #define PHONON_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ |
247 | static QBasicAtomicPointer<TYPE > _k_static_##NAME = Q_BASIC_ATOMIC_INITIALIZER(nullptr); \ |
248 | static bool _k_static_##NAME##_destroyed; \ |
249 | static struct PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) \ |
250 | { \ |
251 | inline bool isDestroyed() const \ |
252 | { \ |
253 | return _k_static_##NAME##_destroyed; \ |
254 | } \ |
255 | inline bool exists() const \ |
256 | { \ |
257 | return _k_static_##NAME.loadRelaxed() != nullptr; \ |
258 | } \ |
259 | inline operator TYPE*() \ |
260 | { \ |
261 | return operator->(); \ |
262 | } \ |
263 | inline TYPE *operator->() \ |
264 | { \ |
265 | if (!_k_static_##NAME.loadRelaxed()) { \ |
266 | if (isDestroyed()) { \ |
267 | qFatal("Fatal Error: Accessed global static '%s *%s()' after destruction. " \ |
268 | "Defined at %s:%d", #TYPE, #NAME, __FILE__, __LINE__); \ |
269 | } \ |
270 | TYPE *x = new TYPE ARGS; \ |
271 | if (!_k_static_##NAME.testAndSetOrdered(nullptr, x) \ |
272 | && _k_static_##NAME.loadRelaxed() != x ) { \ |
273 | delete x; \ |
274 | } else { \ |
275 | static Phonon::CleanUpGlobalStatic cleanUpObject = { destroy }; \ |
276 | } \ |
277 | } \ |
278 | return _k_static_##NAME.loadRelaxed(); \ |
279 | } \ |
280 | inline TYPE &operator*() \ |
281 | { \ |
282 | return *operator->(); \ |
283 | } \ |
284 | static void destroy() \ |
285 | { \ |
286 | _k_static_##NAME##_destroyed = true; \ |
287 | TYPE *x = _k_static_##NAME.loadRelaxed(); \ |
288 | _k_static_##NAME.storeRelaxed(nullptr); \ |
289 | delete x; \ |
290 | } \ |
291 | } NAME; |
292 | |
293 | #endif // PHONON_GLOBALSTATIC_P_H |
294 | |