Audacity 3.2.0
AttachedVirtualFunction.h
Go to the documentation of this file.
1/*!********************************************************************
2
3Audacity: A Digital Audio Editor
4
5@file AttachedVirtualFunction.h
6@brief Utility for non-intrusive definition of a new method on a base class
7
8Paul Licameli
9
10**********************************************************************/
11
12#ifndef __AUDACITY_ATTACHED_VIRTUAL_FUNCTION__
13#define __AUDACITY_ATTACHED_VIRTUAL_FUNCTION__
14
15
16#include <functional>
17#include <mutex>
18#include <type_traits>
19#include <utility>
21
23
157template< typename Tag, typename Return, typename This, typename... Arguments >
159{
160public:
161
163 using Object = This;
165 using Function = std::function< Return( Object&, Arguments... ) >;
167
171
173
175
177
181 template<
182 typename Subclass, typename Overridden = AttachedVirtualFunction >
183 struct Override : Overridden
184 {
186 using Object = Subclass;
188 using Function = std::function< Return( Object&, Arguments... ) >;
189
190 // Check that inheritance is correct
191 static_assert(
192 std::is_base_of_v< typename Overridden::Object, Object >,
193 "overridden class must be a base of the overriding class"
194 );
195
199 static Return Callthrough(
200 typename Overridden::Object &object, Arguments &&...arguments )
201 {
202 return Overridden::Implementation()(
203 object, std::forward< Arguments >( arguments )... );
204 }
206
208 {
209 static std::once_flag flag;
210 std::call_once( flag, []{
211 // Register in the table an adaptor thunk that downcasts the object
212 auto implementation = Implementation();
213 Register< Subclass >( [=]( This &obj, Arguments ...arguments ){
214 return implementation(
215 static_cast< Subclass& >( obj ),
216 std::forward< Arguments >( arguments )... );
217 });
218 });
219 }
220 };
221
223 static Return Call(
224 This &obj,
225 Arguments ...arguments
226 )
227 {
228 try {
229 // Note that the constructors of this class and overrides cause
230 // the registry to be topologically sorted, with functions for
231 // less-derived classes earlier in the table; so take the last
232 // one that matches the object. (The object might not be of the exact
233 // class corresponding to any of the overrides, which is why this
234 // solution involves calling the predicates generated in Register,
235 // and wouldn't work just with hashing on std::type_index; but perhaps
236 // such a cache could be memo-ized)
237 auto &registry = GetRegistry();
238 auto iter = registry.rbegin(), end = registry.rend();
239 for ( ; iter != end; ++iter ) {
240 auto &entry = *iter;
241 if ( entry.predicate( &obj ) )
242 // This might throw std::bad_function_call on a null function
243 return entry.function(
244 obj, std::forward< Arguments >( arguments )... );
245 }
246 // If not found, also throw
247 throw std::bad_function_call{};
248 }
249 catch ( const std::bad_function_call& ) {
250 // No matching case found with a non-null function.
251 // Translate the exception
253 }
254 }
255
256private:
257 template< typename Subclass >
258 static void Register( const Function &function )
259 {
260 // Push back a dynamic type test and corresponding function body
261 GetRegistry().push_back({
262 []( This *b ){ return dynamic_cast< Subclass * >( b ) != nullptr; },
263 function
264 });
265 }
266
267 using Predicate = std::function< bool( This* ) >;
268
270 struct Entry
271 {
274 };
275
276 using Registry = std::vector< Entry >;
278};
279
281#define DECLARE_EXPORTED_ATTACHED_VIRTUAL(DECLSPEC, Name) \
282 template<> DECLSPEC Name::AttachedVirtualFunction(); \
283 template<> auto DECLSPEC Name::GetRegistry() -> Registry &; \
284 template<> auto DECLSPEC Name::Implementation() -> Function
285
287#define DEFINE_ATTACHED_VIRTUAL(Name) \
288 template<> Name::AttachedVirtualFunction() \
289 { \
290 static std::once_flag flag; \
291 std::call_once( flag, []{ Register<Object>( Implementation() ); } ); \
292 } \
293 template<> auto Name::GetRegistry() -> Registry & \
294 { \
295 static Registry registry; \
296 return registry; \
297 } \
298 static Name register ## Name ; \
299 template<> auto Name::Implementation() -> Function
300
302#define DEFINE_ATTACHED_VIRTUAL_OVERRIDE(Name) \
303 static Name register ## Name ; \
304 template<> template<> auto Name::Implementation() -> Function \
305
306#endif
static ProjectFileIORegistry::AttributeWriterEntry entry
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
static std::once_flag flag
Class template generates single-dispatch, open method registry tables.
static Registry & GetRegistry()
static Return Call(This &obj, Arguments ...arguments)
Invoke the method – but only after static initialization time.
static void Register(const Function &function)
static Function Implementation()
A function returning a std::function, which you must define so that the program links.
AttachedVirtualFunction()
At least one static instance must be created; more instances are harmless.
This Object
This member name is declared in this class template and redeclared in each override.
std::function< Return(Object &, Arguments...) > Function
This member name is declared in this class template and redeclared in each override.
std::function< bool(This *) > Predicate
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
Member of registry of implementations of the method.
Function function
Predicate predicate
For defining overrides of the method.
static Function Implementation()
A function returning a std::function that must be defined so that the program links.
static Return Callthrough(typename Overridden::Object &object, Arguments &&...arguments)
May be used in the body of the overriding function, defining it in terms of the overridden one.
Override()
At least one static instance must be created; more instances are harmless.