Audacity 3.2.0
ClientData.h
Go to the documentation of this file.
1/*!********************************************************************
2
3Audacity: 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
8Paul 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>
22
24namespace ClientData {
25
27struct REGISTRIES_API Base
28{
29 virtual ~Base();
30};
31
33
34template< typename Object > using UniquePtr = std::unique_ptr< Object >;
35
37template< typename Object > using BarePtr = Object*;
38
40
45template<
46 typename Covariant = void, // CRTP derived class when not void
47 template<typename> class Owner = UniquePtr
48> struct REGISTRIES_API Cloneable
49{
50 using Base = std::conditional_t<
51 std::is_void_v<Covariant>, Cloneable, Covariant
52 >;
53 using PointerType = Owner< Base >;
54
55 Cloneable() = default;
56 Cloneable(const Cloneable&) = default;
57 Cloneable &operator=(const Cloneable &) = default;
58 virtual ~Cloneable() = default;
59 virtual PointerType Clone() const = 0;
60};
61
62extern template struct REGISTRIES_API Cloneable<>;
63
65
214template<
215 typename Host,
216 typename ClientData = Base,
217
218 // Remaining parameters are often defaulted
219
220 CopyingPolicy ObjectCopyingPolicy = SkipCopying,
221
222 template<typename> class Pointer = UniquePtr,
223
224 LockingPolicy ObjectLockingPolicy = NoLocking,
225 LockingPolicy RegistryLockingPolicy = NoLocking
226>
227class Site
228{
229public:
231 {
232 static_assert( std::has_virtual_destructor<ClientData>::value,
233 "ClientData::Site requires a data class with a virtual destructor" );
234 }
235
237 using DataPointer = Pointer< ClientData >;
239 using DataFactory = std::function< DataPointer( Host& ) >;
240
242 {
243 auto factories = GetFactories();
244 auto size = factories.mObject.size();
245 mData.reserve( size );
246 }
247 Site( const Site &other )
248 : mData( other.mData )
249 { }
250 Site& operator =( const Site & other )
251 { mData = other.mData; return *this; }
252 Site( Site && other )
253 : mData( std::move(other.mData) )
254 { }
255 Site& operator =( Site && other )
256 { mData = std::move(other.mData); return *this; }
257
259 size_t size() const { return mData.size(); }
260
262
266 static size_t numFactories() { return GetFactories().mObject.size(); }
267
269
274 {
275 public:
278 )
279 {
280 auto factories = GetFactories();
281 mIndex = factories.mObject.size();
282 factories.mObject.emplace_back( std::move( factory ) );
283 }
285 {
286 mIndex = other.mIndex;
287 mOwner = other.mOwner;
288 other.mOwner = false;
289 }
291 {
292 if (mOwner) {
293 auto factories = GetFactories();
294 // Should always be true, the factory vector never shrinks:
295 if ( mIndex < factories.mObject.size() )
296 factories.mObject[mIndex] = nullptr;
297 }
298 }
299 private:
300 friend Site;
301 bool mOwner{ true };
302 size_t mIndex;
303 };
304
307
309
316 template< typename Subclass = ClientData >
317 Subclass &Get( const RegisteredFactory &key )
318 {
319 auto data = GetData();
320 return DoGet< Subclass >( data, key );
321 }
322
324
325 template< typename Subclass = const ClientData >
326 auto Get( const RegisteredFactory &key ) const ->
327 std::enable_if_t< std::is_const< Subclass >::value, Subclass & >
328 {
329 auto data = GetData();
330 return DoGet< Subclass >( data, key );
331 }
332
334
340 template< typename Subclass = ClientData >
341 Subclass *Find( const RegisteredFactory &key )
342 {
343 auto data = GetData();
344 return DoFind< Subclass >( data, key );
345 }
346
348
349 template< typename Subclass = const ClientData >
350 auto Find( const RegisteredFactory &key ) const ->
351 std::enable_if_t< std::is_const< Subclass >::value, Subclass * >
352 {
353 auto data = GetData();
354 return DoFind< Subclass >( data, key );
355 }
356
358
362 template< typename ReplacementPointer >
363 void Assign(
364 const RegisteredFactory &key,
365 ReplacementPointer &&replacement
366 )
367 {
368 auto index = key.mIndex;
369 auto data = GetData();
370 EnsureIndex( data, index );
371 auto iter = GetIterator( data, index );
372 // Copy or move as appropriate:
373 *iter = std::forward< ReplacementPointer >( replacement );
374 }
375
377
378protected:
381
383
387 template< typename Function >
388 void ForEach( const Function &function )
389 {
390 auto data = GetData();
391 for( auto &pObject : data.mObject ) {
392 const auto &ptr = Dereferenceable(pObject);
393 if ( ptr )
394 function( *ptr );
395 }
396 }
397
399
400 template< typename Function >
401 void ForEach( const Function &function ) const
402 {
403 auto data = GetData();
404 for( auto &pObject : data.mObject ) {
405 const auto &ptr = Dereferenceable(pObject);
406 if ( ptr ) {
407 const auto &c_ref = *ptr;
408 function( c_ref );
409 }
410 }
411 }
412
414
419 template< typename Function >
420 ClientData *FindIf( const Function &function )
421 {
422 auto data = GetData();
423 for( auto &pObject : data.mObject ) {
424 const auto &ptr = Dereferenceable(pObject);
425 if ( ptr && function ( *ptr ) )
426 return &*ptr;
427 }
428 return nullptr;
429 }
430
432
433 template< typename Function >
434 const ClientData *FindIf( const Function &function ) const
435 {
436 auto data = GetData();
437 for( auto &pObject : data.mObject ) {
438 const auto &ptr = Dereferenceable(pObject);
439 if ( ptr ) {
440 const auto &c_ref = *ptr;
441 if ( function( c_ref ) )
442 return &*c_ref;
443 }
444 }
445 return nullptr;
446 }
447
449 void BuildAll()
450 {
451 // Note that we cannot call this function in the Site constructor as we
452 // might wish, because we pass *this to the factories, but this is not yet
453 // fully constructed as the ultimate derived class. So delayed calls to
454 // this function are needed.
455 size_t size;
456 {
457 auto factories = GetFactories();
458 size = factories.mObject.size();
459 // Release lock on factories before getting one on data -- otherwise
460 // there would be a deadlock possibility inside Ensure
461 }
462 auto data = GetData();
463 EnsureIndex( data, size - 1 );
464 auto iter = GetIterator( data, 0 );
465 for ( size_t ii = 0; ii < size; ++ii, ++iter )
466 static_cast< void >( Build( data, iter, ii ) );
467 }
468
470
471private:
473 Lockable< std::vector< DataFactory >, RegistryLockingPolicy >;
475 Lockable<
476 Copyable< std::vector< DataPointer >, ObjectCopyingPolicy >,
477 ObjectLockingPolicy
478 >;
479
480 decltype( Dereferenceable( std::declval<DataPointer&>() ) )
481 Slot( Locked<DataContainer> &data, const RegisteredFactory &key, bool create )
482 {
483 auto index = key.mIndex;
484 EnsureIndex( data, index );
485 auto iter = GetIterator( data, index );
486 auto &pointer = create ? Build( data, iter, index ) : *iter;
487 return Dereferenceable( pointer );
488 }
489
490 template< typename Subclass >
492 {
493 const auto &d = Slot( data, key, true );
494 if (!d)
495 // Oops, a factory was deregistered too soon, or returned a null, or
496 // the pointer was reassigned null
498 return static_cast< Subclass& >( *d );
499 }
500
501 template< typename Subclass >
503 {
504 const auto &d = Slot( data, key, false );
505 if (!d)
506 return nullptr;
507 else
508 return static_cast< Subclass* >( &*d );
509 }
510
512 {
513 // C++11 does not need extra thread synch for static initialization
514 // Note that linker eliminates duplicates of this function
515 static DataFactories factories;
516
517 // But give back a scoped lock to the user of this function, in case
518 // there is contention to resize the vector
519 return Locked< DataFactories >{ factories };
520 }
521
523 {
525 }
526
528 {
530 }
531
532 static void EnsureIndex( Locked<DataContainer> &data, size_t index )
533 {
534 if (data.mObject.size() <= index)
535 data.mObject.resize(index + 1);
536 }
537
538 static typename DataContainer::iterator inline
539 GetIterator( Locked<DataContainer> &data, size_t index )
540 {
541 // This function might help generalize Site with different kinds of
542 // containers for pointers to ClientData that are not random-access.
543 // Perhaps non-relocation of elements will be needed.
544 // Perhaps another template-template parameter could vary the kind of
545 // container.
546 auto result = data.mObject.begin();
547 std::advance( result, index );
548 return result;
549 }
550
552 typename DataContainer::iterator iter, size_t index )
553 {
554 // If there is no object at index, then invoke the factory, else do
555 // nothing.
556 // The factory may be null or may return null, in which case do nothing.
557 auto &result = *iter;
558 if (!Dereferenceable(result)) {
559 // creation on demand
560 auto factories = GetFactories();
561 auto &factory = factories.mObject[index];
562 result = factory
563 ? factory( static_cast< Host& >( *this ) )
564 : DataPointer{};
565 }
566 return result;
567 }
568
570
572};
573
574}
575
576#endif
Some implementation details for ClientData.
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
static const AudacityProject::AttachedObjects::RegisteredFactory key
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:274
RegisteredFactory(RegisteredFactory &&other)
Definition: ClientData.h:284
RegisteredFactory(DataFactory factory)
Definition: ClientData.h:276
Utility to register hooks into a host class that attach client data.
Definition: ClientData.h:228
size_t size() const
How many attachment pointers are in the Site.
Definition: ClientData.h:259
Site & operator=(const Site &other)
Definition: ClientData.h:250
Site(const Site &other)
Definition: ClientData.h:247
ClientData * FindIf(const Function &function)
Return pointer to first attachment in this that is not null and satisfies a predicate,...
Definition: ClientData.h:420
Pointer< ClientData > DataPointer
Definition: ClientData.h:237
static Locked< DataFactories > GetFactories()
Definition: ClientData.h:511
Locked< DataContainer > GetData()
Definition: ClientData.h:522
DataContainer mData
Container of pointers returned by factories, per instance of Host class.
Definition: ClientData.h:571
Subclass * DoFind(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:502
static size_t numFactories()
How many static factories have been registered with this specialization of Site.
Definition: ClientData.h:266
static DataContainer::iterator GetIterator(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:539
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:341
static void EnsureIndex(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:532
Locked< const DataContainer > GetData() const
Definition: ClientData.h:527
ClientData DataType
Definition: ClientData.h:236
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:388
void ForEach(const Function &function) const
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:401
Site(Site &&other)
Definition: ClientData.h:252
DataPointer & Build(Locked< DataContainer > &, typename DataContainer::iterator iter, size_t index)
Definition: ClientData.h:551
void BuildAll()
For each RegisteredFactory, if the corresponding attachment is absent in this, build and store it.
Definition: ClientData.h:449
Subclass & DoGet(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:491
std::function< DataPointer(Host &) > DataFactory
Type of function from which RegisteredFactory is constructed; it builds attachments.
Definition: ClientData.h:239
auto Find(const RegisteredFactory &key) const -> std::enable_if_t< std::is_const< Subclass >::value, Subclass * >
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Definition: ClientData.h:350
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:434
auto Get(const RegisteredFactory &key) const -> std::enable_if_t< std::is_const< Subclass >::value, Subclass & >
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:326
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:317
void Assign(const RegisteredFactory &key, ReplacementPointer &&replacement)
Reassign Site's pointer to ClientData.
Definition: ClientData.h:363
decltype(Dereferenceable(std::declval< DataPointer & >())) Slot(Locked< DataContainer > &data, const RegisteredFactory &key, bool create)
Definition: ClientData.h:481
Utility ClientData::Site to register hooks into a host class that attach client data.
Definition: ClientData.h:24
static const Ptr & Dereferenceable(Ptr &p)
Conversion allowing operator * on any Pointer parameter of ClientData::Site.
Object * BarePtr
This template-template parameter for ClientData::Site risks dangling pointers, so be careful.
Definition: ClientData.h:37
CopyingPolicy
Statically specify how the ClientData::Site implements its copy constructor and assignment.
@ SkipCopying
ignore the source and leave empty
LockingPolicy
Statically specify whether there is mutual exclusion (separately for the table of factories,...
std::unique_ptr< Object > UniquePtr
A one-argument alias template for the default template-template parameter of ClientData::Site.
Definition: ClientData.h:34
static RegisteredToolbarFactory factory
STL namespace.
A convenient default parameter for class template Site.
Definition: ClientData.h:28
A convenient base class defining abstract virtual Clone() for a given kind of pointer.
Definition: ClientData.h:49
virtual PointerType Clone() const =0
Cloneable(const Cloneable &)=default
std::conditional_t< std::is_void_v< Covariant >, Cloneable, Covariant > Base
Definition: ClientData.h:52
Owner< Base > PointerType
Definition: ClientData.h:53
virtual ~Cloneable()=default
Cloneable & operator=(const Cloneable &)=default
Decorator template injects copy and move operators for container of pointers.
Decorator template injects type Lock and method lock() into interface of Object.
Decorated reference to a ClientData::Lockable, with a current lock on it.