Audacity  3.0.3
ClientData.h
Go to the documentation of this file.
1 /*!********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 @file ClientData.h
6 @brief Utility ClientData::Site to register hooks into a host class that attach client data
7 
8 Paul Licameli
9 
10 **********************************************************************/
11 
12 #ifndef __AUDACITY_CLIENT_DATA__
13 #define __AUDACITY_CLIENT_DATA__
14 
15 #include "ClientDataHelpers.h"
16 
17 #include <functional>
18 #include <iterator>
19 #include <utility>
20 #include <vector>
21 #include "InconsistencyException.h"
22 
24 namespace ClientData {
25 
27 struct REGISTRIES_API Base
28 {
29  virtual ~Base();
30 };
31 
33 
34 template< typename Object > using UniquePtr = std::unique_ptr< Object >;
35 
37 template< typename Object > using BarePtr = Object*;
38 
40 
45 template<
46  template<typename> class Owner = UniquePtr
47 > struct REGISTRIES_API Cloneable
48 {
49  using Base = Cloneable;
50  using PointerType = Owner< Base >;
51 
52  virtual ~Cloneable();
53  virtual PointerType Clone() const = 0;
54 };
55 
57 
206 template<
207  typename Host,
208  typename ClientData = Base,
209 
210  // Remaining parameters are often defaulted
211 
212  CopyingPolicy ObjectCopyingPolicy = SkipCopying,
213 
214  template<typename> class Pointer = UniquePtr,
215 
216  LockingPolicy ObjectLockingPolicy = NoLocking,
217  LockingPolicy RegistryLockingPolicy = NoLocking
218 >
219 class Site
220 {
221 public:
223  {
224  static_assert( std::has_virtual_destructor<ClientData>::value,
225  "ClientData::Site requires a data class with a virtual destructor" );
226  }
227 
229  using DataPointer = Pointer< ClientData >;
231  using DataFactory = std::function< DataPointer( Host& ) >;
232 
234  {
235  auto factories = GetFactories();
236  auto size = factories.mObject.size();
237  mData.reserve( size );
238  }
239  Site( const Site &other )
240  : mData( other.mData )
241  { }
242  Site& operator =( const Site & other )
243  { mData = other.mData; return *this; }
244  Site( Site && other )
245  : mData( std::move(other.mData) )
246  { }
247  Site& operator =( Site && other )
248  { mData = std::move(other.mData); return *this; }
249 
251  size_t size() const { return mData.size(); }
252 
254 
258  static size_t slots() { return GetFactories().mObject.size(); }
259 
261 
266  {
267  public:
270  )
271  {
272  auto factories = GetFactories();
273  mIndex = factories.mObject.size();
274  factories.mObject.emplace_back( std::move( factory ) );
275  }
277  {
278  mIndex = other.mIndex;
279  mOwner = other.mOwner;
280  other.mOwner = false;
281  }
283  {
284  if (mOwner) {
285  auto factories = GetFactories();
286  // Should always be true, the factory vector never shrinks:
287  if ( mIndex < factories.mObject.size() )
288  factories.mObject[mIndex] = nullptr;
289  }
290  }
291  private:
292  friend Site;
293  bool mOwner{ true };
294  size_t mIndex;
295  };
296 
299 
301 
308  template< typename Subclass = ClientData >
309  Subclass &Get( const RegisteredFactory &key )
310  {
311  auto data = GetData();
312  return DoGet< Subclass >( data, key );
313  }
314 
316 
317  template< typename Subclass = const ClientData >
318  auto Get( const RegisteredFactory &key ) const -> typename
319  std::enable_if< std::is_const< Subclass >::value, Subclass & >::type
320  {
321  auto data = GetData();
322  return DoGet< Subclass >( data, key );
323  }
324 
326 
332  template< typename Subclass = ClientData >
333  Subclass *Find( const RegisteredFactory &key )
334  {
335  auto data = GetData();
336  return DoFind< Subclass >( data, key );
337  }
338 
340 
341  template< typename Subclass = const ClientData >
342  auto Find( const RegisteredFactory &key ) const -> typename
343  std::enable_if< std::is_const< Subclass >::value, Subclass * >::type
344  {
345  auto data = GetData();
346  return DoFind< Subclass >( data, key );
347  }
348 
350 
354  template< typename ReplacementPointer >
355  void Assign(
356  const RegisteredFactory &key,
357  ReplacementPointer &&replacement
358  )
359  {
360  auto index = key.mIndex;
361  auto data = GetData();
362  EnsureIndex( data, index );
363  auto iter = GetIterator( data, index );
364  // Copy or move as appropriate:
365  *iter = std::forward< ReplacementPointer >( replacement );
366  }
367 
369 
370 protected:
373 
375 
379  template< typename Function >
380  void ForEach( const Function &function )
381  {
382  auto data = GetData();
383  for( auto &pObject : data.mObject ) {
384  const auto &ptr = Dereferenceable(pObject);
385  if ( ptr )
386  function( *ptr );
387  }
388  }
389 
391 
392  template< typename Function >
393  void ForEach( const Function &function ) const
394  {
395  auto data = GetData();
396  for( auto &pObject : data.mObject ) {
397  const auto &ptr = Dereferenceable(pObject);
398  if ( ptr ) {
399  const auto &c_ref = *ptr;
400  function( c_ref );
401  }
402  }
403  }
404 
406 
411  template< typename Function >
412  ClientData *FindIf( const Function &function )
413  {
414  auto data = GetData();
415  for( auto &pObject : data.mObject ) {
416  const auto &ptr = Dereferenceable(pObject);
417  if ( ptr && function ( *ptr ) )
418  return &*ptr;
419  }
420  return nullptr;
421  }
422 
424 
425  template< typename Function >
426  const ClientData *FindIf( const Function &function ) const
427  {
428  auto data = GetData();
429  for( auto &pObject : data.mObject ) {
430  const auto &ptr = Dereferenceable(pObject);
431  if ( ptr ) {
432  const auto &c_ref = *ptr;
433  if ( function( c_ref ) )
434  return &*c_ref;
435  }
436  }
437  return nullptr;
438  }
439 
441  void BuildAll()
442  {
443  // Note that we cannot call this function in the Site constructor as we
444  // might wish, because we pass *this to the factories, but this is not yet
445  // fully constructed as the ultimate derived class. So delayed calls to
446  // this function are needed.
447  size_t size;
448  {
449  auto factories = GetFactories();
450  size = factories.mObject.size();
451  // Release lock on factories before getting one on data -- otherwise
452  // there would be a deadlock possibility inside Ensure
453  }
454  auto data = GetData();
455  EnsureIndex( data, size - 1 );
456  auto iter = GetIterator( data, 0 );
457  for ( size_t ii = 0; ii < size; ++ii, ++iter )
458  static_cast< void >( Build( data, iter, ii ) );
459  }
460 
462 
463 private:
465  Lockable< std::vector< DataFactory >, RegistryLockingPolicy >;
467  Lockable<
468  Copyable< std::vector< DataPointer >, ObjectCopyingPolicy >,
469  ObjectLockingPolicy
470  >;
471 
472  decltype( Dereferenceable( std::declval<DataPointer&>() ) )
473  Slot( Locked<DataContainer> &data, const RegisteredFactory &key, bool create )
474  {
475  auto index = key.mIndex;
476  EnsureIndex( data, index );
477  auto iter = GetIterator( data, index );
478  auto &pointer = create ? Build( data, iter, index ) : *iter;
479  return Dereferenceable( pointer );
480  }
481 
482  template< typename Subclass >
483  Subclass &DoGet( Locked<DataContainer> &data, const RegisteredFactory &key )
484  {
485  const auto &d = Slot( data, key, true );
486  if (!d)
487  // Oops, a factory was deregistered too soon, or returned a null, or
488  // the pointer was reassigned null
490  return static_cast< Subclass& >( *d );
491  }
492 
493  template< typename Subclass >
494  Subclass *DoFind( Locked<DataContainer> &data, const RegisteredFactory &key )
495  {
496  const auto &d = Slot( data, key, false );
497  if (!d)
498  return nullptr;
499  else
500  return static_cast< Subclass* >( &*d );
501  }
502 
504  {
505  // C++11 does not need extra thread synch for static initialization
506  // Note that linker eliminates duplicates of this function
507  static DataFactories factories;
508 
509  // But give back a scoped lock to the user of this function, in case
510  // there is contention to resize the vector
511  return Locked< DataFactories >{ factories };
512  }
513 
515  {
516  return Locked< DataContainer >{ mData };
517  }
518 
520  {
522  }
523 
524  static void EnsureIndex( Locked<DataContainer> &data, size_t index )
525  {
526  if (data.mObject.size() <= index)
527  data.mObject.resize(index + 1);
528  }
529 
530  static typename DataContainer::iterator inline
531  GetIterator( Locked<DataContainer> &data, size_t index )
532  {
533  // This function might help generalize Site with different kinds of
534  // containers for pointers to ClientData that are not random-access.
535  // Perhaps non-relocation of elements will be needed.
536  // Perhaps another template-template parameter could vary the kind of
537  // container.
538  auto result = data.mObject.begin();
539  std::advance( result, index );
540  return result;
541  }
542 
544  typename DataContainer::iterator iter, size_t index )
545  {
546  // If there is no object at index, then invoke the factory, else do
547  // nothing.
548  // The factory may be null or may return null, in which case do nothing.
549  auto &result = *iter;
550  if (!Dereferenceable(result)) {
551  // creation on demand
552  auto factories = GetFactories();
553  auto &factory = factories.mObject[index];
554  result = factory
555  ? factory( static_cast< Host& >( *this ) )
556  : DataPointer{};
557  }
558  return result;
559  }
560 
562 
564 };
565 
566 }
567 
568 #endif
ClientData::Site::Site
Site()
Definition: ClientData.h:233
ClientData::Locked
Decorated reference to a ClientData::Lockable, with a current lock on it.
Definition: ClientDataHelpers.h:85
ClientData::Site::GetIterator
static DataContainer::iterator GetIterator(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:531
ClientData::Site::ForEach
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:380
ClientData::Site::slots
static size_t slots()
How many static factories have been registered with this specialization of Site.
Definition: ClientData.h:258
ClientData::Locked::mObject
Lockable & mObject
Definition: ClientDataHelpers.h:90
ClientData::Site::Find
Subclass * Find(const RegisteredFactory &key)
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Definition: ClientData.h:333
ClientData::Site::DoFind
Subclass * DoFind(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:494
InconsistencyException.h
MessageBoxException for violation of preconditions or assertions.
ClientData::Site::FindIf
ClientData * FindIf(const Function &function)
Return pointer to first attachment in this that is not null and satisfies a predicate,...
Definition: ClientData.h:412
ClientData::Site::Get
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:309
ClientData::BarePtr
Object * BarePtr
This template-template parameter for ClientData::Site risks dangling pointers, so be careful.
Definition: ClientData.h:37
ClientData::Site::FindIf
const ClientData * FindIf(const Function &function) const
Return pointer to first attachment in this that is not null and satisfies a predicate,...
Definition: ClientData.h:426
ClientData::Site::RegisteredFactory::mOwner
bool mOwner
Definition: ClientData.h:293
ClientData::UniquePtr
std::unique_ptr< Object > UniquePtr
A one-argument alias template for the default template-template parameter of ClientData::Site.
Definition: ClientData.h:34
ClientData::Site::size
size_t size() const
How many attachment pointers are in the Site.
Definition: ClientData.h:251
ClientData::Base
A convenient default parameter for class template Site.
Definition: ClientData.h:28
ClientData::Site::Find
auto Find(const RegisteredFactory &key) const -> typename std::enable_if< std::is_const< Subclass >::value, Subclass * >::type
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Definition: ClientData.h:342
ClientData::Base::~Base
virtual ~Base()
ClientData::Site::RegisteredFactory::mIndex
size_t mIndex
Definition: ClientData.h:294
ClientData::Site::mData
DataContainer mData
Container of pointers returned by factories, per instance of Host class.
Definition: ClientData.h:563
ClientData::Site::RegisteredFactory
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
ClientData::SkipCopying
@ SkipCopying
ignore the source and leave empty
Definition: ClientDataHelpers.h:34
ClientData::LockingPolicy
LockingPolicy
Statically specify whether there is mutual exclusion (separately for the table of factories,...
Definition: ClientDataHelpers.h:24
ClientData::Lockable
Decorator template injects type Lock and method lock() into interface of Object.
Definition: ClientDataHelpers.h:60
ClientData::Site::Assign
void Assign(const RegisteredFactory &key, ReplacementPointer &&replacement)
Reassign Site's pointer to ClientData.
Definition: ClientData.h:355
ClientDataHelpers.h
Some implementation details for ClientData.
ClientData::NoLocking
@ NoLocking
Definition: ClientDataHelpers.h:25
ClientData::Site::EnsureIndex
static void EnsureIndex(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:524
ClientData::Site::Site
Site(const Site &other)
Definition: ClientData.h:239
ClientData::Site::Get
auto Get(const RegisteredFactory &key) const -> typename std::enable_if< std::is_const< Subclass >::value, Subclass & >::type
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:318
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:806
ClientData::Site::DoGet
Subclass & DoGet(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:483
ClientData::Site::Slot
decltype(Dereferenceable(std::declval< DataPointer & >())) Slot(Locked< DataContainer > &data, const RegisteredFactory &key, bool create)
Definition: ClientData.h:473
ClientData::Site< AttachedWindows, wxWindow, ClientData::SkipCopying, ClientData::BarePtr >::DataType
wxWindow DataType
Definition: ClientData.h:228
ClientData::Cloneable::Clone
virtual PointerType Clone() const =0
ClientData::Site
Utility to register hooks into a host class that attach client data.
Definition: ClientData.h:220
ClientData::Site::RegisteredFactory::~RegisteredFactory
~RegisteredFactory()
Definition: ClientData.h:282
THROW_INCONSISTENCY_EXCEPTION
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:79
ClientData::Cloneable
A convenient base class defining abstract virtual Clone() for a given kind of pointer.
Definition: ClientData.h:48
ClientData::Site::RegisteredFactory::RegisteredFactory
RegisteredFactory(DataFactory factory)
Definition: ClientData.h:268
ClientData::Cloneable::PointerType
Owner< Base > PointerType
Definition: ClientData.h:50
ClientData::Site::GetFactories
static Locked< DataFactories > GetFactories()
Definition: ClientData.h:503
ClientData::Copyable
Decorator template injects copy and move operators for container of pointers.
Definition: ClientDataHelpers.h:94
ClientData::Site::GetData
Locked< DataContainer > GetData()
Definition: ClientData.h:514
ClientData::Site::operator=
Site & operator=(const Site &other)
Definition: ClientData.h:242
ClientData::Site::~Site
~Site()
Definition: ClientData.h:222
ClientData::CopyingPolicy
CopyingPolicy
Statically specify how the ClientData::Site implements its copy constructor and assignment.
Definition: ClientDataHelpers.h:33
key
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: CommandManager.cpp:201
ClientData::Site::RegisteredFactory::Site
friend Site
Definition: ClientData.h:292
ClientData::Site::BuildAll
void BuildAll()
For each RegisteredFactory, if the corresponding attachment is absent in this, build and store it.
Definition: ClientData.h:441
ClientData::Cloneable::~Cloneable
virtual ~Cloneable()
ClientData::Site::Build
DataPointer & Build(Locked< DataContainer > &, typename DataContainer::iterator iter, size_t index)
Definition: ClientData.h:543
ClientData::Site::Site
Site(Site &&other)
Definition: ClientData.h:244
ClientData::Site::GetData
Locked< const DataContainer > GetData() const
Definition: ClientData.h:519
ClientData
Utility ClientData::Site to register hooks into a host class that attach client data.
Definition: ClientData.h:24
ClientData::Site::ForEach
void ForEach(const Function &function) const
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:393
ClientData::Site< AttachedWindows, wxWindow, ClientData::SkipCopying, ClientData::BarePtr >::DataPointer
ClientData::BarePtr< wxWindow > DataPointer
Definition: ClientData.h:229
ClientData::Site< AttachedWindows, wxWindow, ClientData::SkipCopying, ClientData::BarePtr >::DataFactory
std::function< DataPointer(AttachedWindows &) > DataFactory
Type of function from which RegisteredFactory is constructed; it builds attachments.
Definition: ClientData.h:231
ClientData::Site::RegisteredFactory::RegisteredFactory
RegisteredFactory(RegisteredFactory &&other)
Definition: ClientData.h:276
ClientData::Dereferenceable
static const Ptr & Dereferenceable(Ptr &p)
Conversion allowing operator * on any Pointer parameter of ClientData::Site.
Definition: ClientDataHelpers.h:48