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 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
206template<
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>
219class Site
220{
221public:
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 ->
319 std::enable_if_t< std::is_const< Subclass >::value, Subclass & >
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 ->
343 std::enable_if_t< std::is_const< Subclass >::value, Subclass * >
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
370protected:
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
463private:
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 >
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 >
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 {
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
Some implementation details for ClientData.
static const AudacityProject::AttachedObjects::RegisteredFactory key
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
RegisteredFactory(RegisteredFactory &&other)
Definition: ClientData.h:276
RegisteredFactory(DataFactory factory)
Definition: ClientData.h:268
Utility to register hooks into a host class that attach client data.
Definition: ClientData.h:220
size_t size() const
How many attachment pointers are in the Site.
Definition: ClientData.h:251
Site & operator=(const Site &other)
Definition: ClientData.h:242
Site(const Site &other)
Definition: ClientData.h:239
ClientData * FindIf(const Function &function)
Return pointer to first attachment in this that is not null and satisfies a predicate,...
Definition: ClientData.h:412
Pointer< ClientData > DataPointer
Definition: ClientData.h:229
static Locked< DataFactories > GetFactories()
Definition: ClientData.h:503
Locked< DataContainer > GetData()
Definition: ClientData.h:514
DataContainer mData
Container of pointers returned by factories, per instance of Host class.
Definition: ClientData.h:563
static size_t slots()
How many static factories have been registered with this specialization of Site.
Definition: ClientData.h:258
Subclass * DoFind(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:494
static DataContainer::iterator GetIterator(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:531
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
static void EnsureIndex(Locked< DataContainer > &data, size_t index)
Definition: ClientData.h:524
Locked< const DataContainer > GetData() const
Definition: ClientData.h:519
ClientData DataType
Definition: ClientData.h:228
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:380
void ForEach(const Function &function) const
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:393
Site(Site &&other)
Definition: ClientData.h:244
DataPointer & Build(Locked< DataContainer > &, typename DataContainer::iterator iter, size_t index)
Definition: ClientData.h:543
void BuildAll()
For each RegisteredFactory, if the corresponding attachment is absent in this, build and store it.
Definition: ClientData.h:441
Subclass & DoGet(Locked< DataContainer > &data, const RegisteredFactory &key)
Definition: ClientData.h:483
std::function< DataPointer(Host &) > DataFactory
Type of function from which RegisteredFactory is constructed; it builds attachments.
Definition: ClientData.h:231
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:342
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
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:318
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
void Assign(const RegisteredFactory &key, ReplacementPointer &&replacement)
Reassign Site's pointer to ClientData.
Definition: ClientData.h:355
decltype(Dereferenceable(std::declval< DataPointer & >())) Slot(Locked< DataContainer > &data, const RegisteredFactory &key, bool create)
Definition: ClientData.h:473
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:48
virtual PointerType Clone() const =0
Owner< Base > PointerType
Definition: ClientData.h:50
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.