Audacity  3.0.3
AttachedVirtualFunction.h
Go to the documentation of this file.
1 /*!********************************************************************
2 
3 Audacity: 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 
8 Paul 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>
20 #include "InconsistencyException.h"
21 
23 
157 template< typename Tag, typename Return, typename This, typename... Arguments >
159 {
160 public:
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< typename Overridden::Object, Object >::value,
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 
256 private:
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  {
273  Function function;
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
AttachedVirtualFunction::Object
This Object
This member name is declared in this class template and redeclared in each override.
Definition: AttachedVirtualFunction.h:163
Registry
Definition: Menus.h:35
AttachedVirtualFunction::Call
static Return Call(This &obj, Arguments &&...arguments)
Invoke the method – but only after static initialization time.
Definition: AttachedVirtualFunction.h:223
flag
static std::once_flag flag
Definition: WaveformView.cpp:1119
AttachedVirtualFunction::Entry
Member of registry of implementations of the method.
Definition: AttachedVirtualFunction.h:271
AttachedVirtualFunction
Class template generates single-dispatch, open method registry tables.
Definition: AttachedVirtualFunction.h:159
InconsistencyException.h
MessageBoxException for violation of preconditions or assertions.
entry
static ProjectFileIORegistry::WriterEntry entry
Definition: ProjectSettings.cpp:197
AttachedVirtualFunction::Register
static void Register(const Function &function)
Definition: AttachedVirtualFunction.h:258
AttachedVirtualFunction::Override::Implementation
static Function Implementation()
A function returning a std::function that must be defined so that the program links.
AttachedVirtualFunction::Entry::predicate
Predicate predicate
Definition: AttachedVirtualFunction.h:272
AttachedVirtualFunction::AttachedVirtualFunction
AttachedVirtualFunction()
At least one static instance must be created; more instances are harmless.
THROW_INCONSISTENCY_EXCEPTION
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:79
AttachedVirtualFunction::Predicate
std::function< bool(This *) > Predicate
Definition: AttachedVirtualFunction.h:267
AttachedVirtualFunction::Implementation
static Function Implementation()
A function returning a std::function, which you must define so that the program links.
AttachedVirtualFunction::Override::Callthrough
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.
Definition: AttachedVirtualFunction.h:199
AttachedVirtualFunction::Override::Override
Override()
At least one static instance must be created; more instances are harmless.
Definition: AttachedVirtualFunction.h:207
AttachedVirtualFunction::Override
For defining overrides of the method.
Definition: AttachedVirtualFunction.h:184
AttachedVirtualFunction::GetRegistry
static Registry & GetRegistry()
AttachedVirtualFunction::Function
std::function< Return(Object &, Arguments...) > Function
This member name is declared in this class template and redeclared in each override.
Definition: AttachedVirtualFunction.h:165