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 <optional>
20#include <utility>
21#include <vector>
23
25namespace ClientData {
26
28struct REGISTRIES_API Base
29{
30 virtual ~Base();
31};
32
34
35template< typename Object > using UniquePtr = std::unique_ptr< Object >;
36
38template< typename Object > using BarePtr = Object*;
39
41
46template<
47 typename Covariant = void, // CRTP derived class when not void
48 template<typename> class Owner = UniquePtr
49> struct REGISTRIES_API Cloneable
50{
51 using Base = std::conditional_t<
52 std::is_void_v<Covariant>, Cloneable, Covariant
53 >;
54 using PointerType = Owner< Base >;
55
56 Cloneable() = default;
57 Cloneable(const Cloneable&) = default;
58 Cloneable &operator=(const Cloneable &) = default;
59 virtual ~Cloneable() = default;
60 virtual PointerType Clone() const = 0;
61};
62
63extern template struct REGISTRIES_API Cloneable<>;
64
66
215template<
216 typename Host,
217 typename ClientData = Base,
218
219 // Remaining parameters are often defaulted
220
221 CopyingPolicy ObjectCopyingPolicy = SkipCopying,
222
223 template<typename> class Pointer = UniquePtr,
224
225 LockingPolicy ObjectLockingPolicy = NoLocking,
226 LockingPolicy RegistryLockingPolicy = NoLocking
227>
228class Site
229{
230public:
232 {
233 static_assert( std::has_virtual_destructor<ClientData>::value,
234 "ClientData::Site requires a data class with a virtual destructor" );
235 }
236
238 using DataPointer = Pointer< ClientData >;
240 using DataFactory = std::function< DataPointer( Host& ) >;
241
243 {
244 auto factories = GetFactories();
245 auto size = factories.mObject.size();
246 mData.reserve( size );
247 }
248 Site( const Site &other )
249 : mData( other.mData )
250 { }
251 Site& operator =( const Site & other )
252 { mData = other.mData; return *this; }
253 Site( Site && other )
254 : mData( std::move(other.mData) )
255 { }
256 Site& operator =( Site && other )
257 { mData = std::move(other.mData); return *this; }
258
260 size_t size() const { return mData.size(); }
261
263
267 static size_t numFactories() { return GetFactories().mObject.size(); }
268
270
275 {
276 public:
279 )
280 {
281 auto factories = GetFactories();
282 mIndex = factories.mObject.size();
283 factories.mObject.emplace_back( std::move( factory ) );
284 }
286 {
287 mIndex = other.mIndex;
288 mOwner = other.mOwner;
289 other.mOwner = false;
290 }
292 {
293 if (mOwner) {
294 auto factories = GetFactories();
295 // Should always be true, the factory vector never shrinks:
296 if ( mIndex < factories.mObject.size() )
297 factories.mObject[mIndex] = nullptr;
298 }
299 }
300 private:
301 friend Site;
302 bool mOwner{ true };
303 size_t mIndex;
304 };
305
308
310
317 template< typename Subclass = ClientData >
318 Subclass &Get( const RegisteredFactory &key )
319 {
320 auto data = GetData();
321 return DoGet< Subclass >( data, key );
322 }
323
325
326 template< typename Subclass = const ClientData >
327 auto Get( const RegisteredFactory &key ) const ->
328 std::enable_if_t< std::is_const< Subclass >::value, Subclass & >
329 {
330 auto data = GetData();
331 return DoGet< Subclass >( data, key );
332 }
333
335
341 template< typename Subclass = ClientData >
342 Subclass *Find( const RegisteredFactory &key )
343 {
344 auto data = GetData();
345 return DoFind< Subclass >( data, key );
346 }
347
349
350 template< typename Subclass = const ClientData >
351 auto Find( const RegisteredFactory &key ) const ->
352 std::enable_if_t< std::is_const< Subclass >::value, Subclass * >
353 {
354 auto data = GetData();
355 return DoFind< Subclass >( data, key );
356 }
357
359
363 template< typename ReplacementPointer >
364 void Assign(
365 const RegisteredFactory &key,
366 ReplacementPointer &&replacement
367 )
368 {
369 auto index = key.mIndex;
370 auto data = GetData();
371 EnsureIndex( data, index );
372 auto iter = GetIterator( data, index );
373 // Copy or move as appropriate:
374 *iter = std::forward< ReplacementPointer >( replacement );
375 }
376
378
379protected:
382
384
388 template< typename Function >
389 void ForEach( const Function &function )
390 {
391 auto data = GetData();
392 for( auto &pObject : data.mObject ) {
393 const auto &ptr = Dereferenceable(pObject);
394 if ( ptr )
395 function( *ptr );
396 }
397 }
398
400
401 template< typename Function >
402 void ForEach( const Function &function ) const
403 {
404 auto data = GetData();
405 for( auto &pObject : data.mObject ) {
406 const auto &ptr = Dereferenceable(pObject);
407 if ( ptr ) {
408 const auto &c_ref = *ptr;
409 function( c_ref );
410 }
411 }
412 }
413
416
427 template<typename Function>
428 void ForCorresponding(Site &other, const Function &function,
429 bool create = true)
430 {
431 size_t size;
432 {
433 auto factories = GetFactories();
434 size = factories.mObject.size();
435 // Release lock on factories before getting one on data -- otherwise
436 // there would be a deadlock possibility inside EnsureIndex
437 }
438
439 // Lock two containers, carefully avoiding deadlock possibility by
440 // ordering them by address in memory
441 std::optional<decltype(GetData())> oOtherData;
442 if (std::addressof(other) < std::addressof(*this))
443 oOtherData.emplace(other.GetData());
444 auto data = GetData();
445 if (!oOtherData)
446 oOtherData.emplace(other.GetData());
447 auto &otherData = *oOtherData;
448
449 // Like BuildAll but needing correspondence
450 EnsureIndex(data, size - 1);
451 EnsureIndex(otherData, size - 1);
452
453 auto iter = GetIterator(data, 0);
454 auto otherIter = GetIterator(otherData, 0);
455
456 for (size_t ii = 0; ii < size; ++ii, ++iter, ++otherIter) {
457 auto &pObject = *iter;
458 auto &pOtherObject = *otherIter;
459 // These lines might lock weak pointers, depending on template
460 // arguments of the class
461 auto deref = &Dereferenceable(pObject);
462 auto otherDeref = &Dereferenceable(pOtherObject);
463 if (!*deref && !*otherDeref)
464 continue;
465 else if (!*deref && create) {
466 // creation on demand
467 auto factories = GetFactories();
468 auto &factory = factories.mObject[ii];
469 pObject = factory
470 ? factory(static_cast<Host&>(*this))
471 : DataPointer{};
472 deref = &Dereferenceable(pObject);
473 }
474 else if (!*otherDeref && create) {
475 // creation on demand
476 auto factories = GetFactories();
477 auto &factory = factories.mObject[ii];
478 pOtherObject = factory
479 ? factory(static_cast<Host&>(other))
480 : DataPointer{};
481 otherDeref = &Dereferenceable(pOtherObject);
482 }
483
484 function(
485 (*deref ? &**deref : nullptr),
486 (*otherDeref ? &**otherDeref : nullptr));
487 }
488 }
489
491
496 template< typename Function >
497 ClientData *FindIf( const Function &function )
498 {
499 auto data = GetData();
500 for( auto &pObject : data.mObject ) {
501 const auto &ptr = Dereferenceable(pObject);
502 if ( ptr && function ( *ptr ) )
503 return &*ptr;
504 }
505 return nullptr;
506 }
507
509
510 template< typename Function >
511 const ClientData *FindIf( const Function &function ) const
512 {
513 auto data = GetData();
514 for( auto &pObject : data.mObject ) {
515 const auto &ptr = Dereferenceable(pObject);
516 if ( ptr ) {
517 const auto &c_ref = *ptr;
518 if ( function( c_ref ) )
519 return &*c_ref;
520 }
521 }
522 return nullptr;
523 }
524
526
531 template<typename Function>
532 void EraseIf(const Function &function)
533 {
534 auto data = GetData();
535 for (auto &pObject : data.mObject) {
536 const auto &ptr = Dereferenceable(pObject);
537 if (ptr) {
538 auto &ref = *ptr;
539 if (function(ref))
540 pObject = nullptr;
541 }
542 }
543 }
544
546 void BuildAll()
547 {
548 // Note that we cannot call this function in the Site constructor as we
549 // might wish, because we pass *this to the factories, but this is not yet
550 // fully constructed as the ultimate derived class. So delayed calls to
551 // this function are needed.
552 size_t size;
553 {
554 auto factories = GetFactories();
555 size = factories.mObject.size();
556 // Release lock on factories before getting one on data -- otherwise
557 // there would be a deadlock possibility inside EnsureIndex
558 }
559 auto data = GetData();
560 EnsureIndex( data, size - 1 );
561 auto iter = GetIterator( data, 0 );
562 for ( size_t ii = 0; ii < size; ++ii, ++iter )
563 static_cast< void >( Build( data, iter, ii ) );
564 }
565
567
568private:
570 Lockable< std::vector< DataFactory >, RegistryLockingPolicy >;
572 Lockable<
573 Copyable< std::vector< DataPointer >, ObjectCopyingPolicy >,
574 ObjectLockingPolicy
575 >;
576
577 decltype( Dereferenceable( std::declval<DataPointer&>() ) )
578 Slot( Locked<DataContainer> &data, const RegisteredFactory &key, bool create )
579 {
580 auto index = key.mIndex;
581 EnsureIndex( data, index );
582 auto iter = GetIterator( data, index );
583 auto &pointer = create ? Build( data, iter, index ) : *iter;
584 return Dereferenceable( pointer );
585 }
586
587 template< typename Subclass >
589 {
590 const auto &d = Slot( data, key, true );
591 if (!d)
592 // Oops, a factory was deregistered too soon, or returned a null, or
593 // the pointer was reassigned null
595 return static_cast< Subclass& >( *d );
596 }
597
598 template< typename Subclass >
600 {
601 const auto &d = Slot( data, key, false );
602 if (!d)
603 return nullptr;
604 else
605 return static_cast< Subclass* >( &*d );
606 }
607
609 {
610 // C++11 does not need extra thread synch for static initialization
611 // Note that linker eliminates duplicates of this function
612 static DataFactories factories;
613
614 // But give back a scoped lock to the user of this function, in case
615 // there is contention to resize the vector
616 return Locked< DataFactories >{ factories };
617 }
618
620 {
622 }
623
625 {
627 }
628
629 static void EnsureIndex( Locked<DataContainer> &data, size_t index )
630 {
631 if (data.mObject.size() <= index)
632 data.mObject.resize(index + 1);
633 }
634
635 static typename DataContainer::iterator inline
636 GetIterator( Locked<DataContainer> &data, size_t index )
637 {
638 // This function might help generalize Site with different kinds of
639 // containers for pointers to ClientData that are not random-access.
640 // Perhaps non-relocation of elements will be needed.
641 // Perhaps another template-template parameter could vary the kind of
642 // container.
643 auto result = data.mObject.begin();
644 std::advance( result, index );
645 return result;
646 }
647
649 typename DataContainer::iterator iter, size_t index )
650 {
651 // If there is no object at index, then invoke the factory, else do
652 // nothing.
653 // The factory may be null or may return null, in which case do nothing.
654 auto &result = *iter;
655 if (!Dereferenceable(result)) {
656 // creation on demand
657 auto factories = GetFactories();
658 auto &factory = factories.mObject[index];
659 result = factory
660 ? factory( static_cast< Host& >( *this ) )
661 : DataPointer{};
662 }
663 return result;
664 }
665
667
669};
670
671}
672
673#endif
static RegisteredToolbarFactory factory
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:275
RegisteredFactory(RegisteredFactory &&other)
Definition: ClientData.h:285
RegisteredFactory(DataFactory factory)
Definition: ClientData.h:277
Utility to register hooks into a host class that attach client data.
Definition: ClientData.h:229
size_t size() const
How many attachment pointers are in the Site.
Definition: ClientData.h:260
Site & operator=(const Site &other)
Definition: ClientData.h:251
Site(const Site &other)
Definition: ClientData.h:248
ClientData * FindIf(const Function &function)
Return pointer to first attachment in this that is not null and satisfies a predicate,...
Definition: ClientData.h:497
Pointer< ClientData > DataPointer
Definition: ClientData.h:238
static Locked< DataFactories > GetFactories()
Definition: ClientData.h:608
Locked< DataContainer > GetData()
Definition: ClientData.h:619
void EraseIf(const Function &function)
Erase attached objects satisfying a predicate.
Definition: ClientData.h:532
DataContainer mData
Container of pointers returned by factories, per instance of Host class.
Definition: ClientData.h:668
Subclass * DoFind(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:599
static size_t numFactories()
How many static factories have been registered with this specialization of Site.
Definition: ClientData.h:267
static DataContainer::iterator GetIterator(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:636
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:342
static void EnsureIndex(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:629
Locked< const DataContainer > GetData() const
Definition: ClientData.h:624
void ForCorresponding(Site &other, const Function &function, bool create=true)
Definition: ClientData.h:428
ClientData DataType
Definition: ClientData.h:237
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:389
void ForEach(const Function &function) const
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:402
Site(Site &&other)
Definition: ClientData.h:253
DataPointer & Build(Locked< DataContainer > &, typename DataContainer::iterator iter, size_t index)
Definition: ClientData.h:648
void BuildAll()
For each RegisteredFactory, if the corresponding attachment is absent in this, build and store it.
Definition: ClientData.h:546
Subclass & DoGet(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:588
std::function< DataPointer(Host &) > DataFactory
Type of function from which RegisteredFactory is constructed; it builds attachments.
Definition: ClientData.h:240
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:351
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:511
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:327
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:318
void Assign(const RegisteredFactory &key, ReplacementPointer &&replacement)
Reassign Site's pointer to ClientData.
Definition: ClientData.h:364
decltype(Dereferenceable(std::declval< DataPointer & >())) Slot(Locked< DataContainer > &data, const RegisteredFactory &key, bool create)
Definition: ClientData.h:578
Utility ClientData::Site to register hooks into a host class that attach client data.
Definition: ClientData.h:25
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:38
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:35
STL namespace.
A convenient default parameter for class template Site.
Definition: ClientData.h:29
A convenient base class defining abstract virtual Clone() for a given kind of pointer.
Definition: ClientData.h:50
virtual PointerType Clone() const =0
Cloneable(const Cloneable &)=default
std::conditional_t< std::is_void_v< Covariant >, Cloneable, Covariant > Base
Definition: ClientData.h:53
Owner< Base > PointerType
Definition: ClientData.h:54
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.