mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-02 17:09:26 +02:00
ClientData
This commit is contained in:
parent
407c83ebf0
commit
11e924a49b
357
src/ClientData.h
357
src/ClientData.h
@ -1,8 +1,9 @@
|
|||||||
/**********************************************************************
|
/*!********************************************************************
|
||||||
|
|
||||||
Audacity: A Digital Audio Editor
|
Audacity: A Digital Audio Editor
|
||||||
|
|
||||||
ClientData.h
|
@file ClientData.h
|
||||||
|
@brief Utility ClientData::Site to register hooks into a host class that attach client data
|
||||||
|
|
||||||
Paul Licameli
|
Paul Licameli
|
||||||
|
|
||||||
@ -19,24 +20,28 @@ Paul Licameli
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "InconsistencyException.h"
|
#include "InconsistencyException.h"
|
||||||
|
|
||||||
|
//! @copydoc ClientData.h
|
||||||
namespace ClientData {
|
namespace ClientData {
|
||||||
|
|
||||||
// A convenient default parameter for class template Site below
|
//! A convenient default parameter for class template @b Site
|
||||||
struct AUDACITY_DLL_API Base
|
struct AUDACITY_DLL_API Base
|
||||||
{
|
{
|
||||||
virtual ~Base() {}
|
virtual ~Base() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Need a truly one-argument alias template below for the default
|
//! A one-argument alias template for the default template-template parameter of ClientData::Site
|
||||||
// template-template argument of Site
|
/*! (std::unique_ptr has two, the second is defaulted) */
|
||||||
// (unique_ptr has two, the second is defaulted)
|
|
||||||
template< typename Object > using UniquePtr = std::unique_ptr< Object >;
|
template< typename Object > using UniquePtr = std::unique_ptr< Object >;
|
||||||
|
|
||||||
// Risk of dangling pointers, so be careful
|
//! This template-template parameter for ClientData::Site risks dangling pointers, so be careful
|
||||||
template< typename Object > using BarePtr = Object*;
|
template< typename Object > using BarePtr = Object*;
|
||||||
|
|
||||||
// A convenient base class defining abstract virtual Clone() for a given kind
|
//! A convenient base class defining abstract virtual Clone() for a given kind of pointer
|
||||||
// of pointer
|
/*!
|
||||||
|
@tparam Owner template-template parameter for the kind of smart pointer, like std::shared_ptr, returned by Clone()
|
||||||
|
|
||||||
|
@sa ClientData::DeepCopying
|
||||||
|
*/
|
||||||
template<
|
template<
|
||||||
template<typename> class Owner = UniquePtr
|
template<typename> class Owner = UniquePtr
|
||||||
> struct Cloneable
|
> struct Cloneable
|
||||||
@ -48,130 +53,156 @@ template<
|
|||||||
virtual PointerType Clone() const = 0;
|
virtual PointerType Clone() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
//! Utility to register hooks into a host class that attach client data
|
||||||
\brief ClientData::Site class template enables decoupling of the
|
/*!
|
||||||
implementation of core data structures, inheriting it, from compilation and
|
This allows the host object to be the root of an ownership tree of sub-objects at
|
||||||
link dependency on the details of other user interface modules, while also
|
run-time, but inverting the compile-time dependency among implementation files:
|
||||||
allowing the latter to associate their own extra data caches with each
|
The host's implementation is in low-level files, and cyclic file dependencies are avoided.
|
||||||
instance of the core class, and the caches can have a coterminous lifetime.
|
The set of client objects attached to each host object is not fixed in the definition of
|
||||||
|
the host class, but instead a system of registration of factories of client objects lets it
|
||||||
|
be open-ended.
|
||||||
|
|
||||||
|
Besides mere storage and retrieval, this can also implement the [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern),
|
||||||
|
in which the host pushes notifications to some virtual function defined in
|
||||||
|
each attached item.
|
||||||
|
|
||||||
This can implement an "observer pattern" in which the core object pushes
|
@par Host side usage pattern
|
||||||
notifications to client-side handlers, or it can just implement storage
|
|
||||||
and retrieval services for the client.
|
|
||||||
|
|
||||||
typical usage pattern in core code:
|
```
|
||||||
|
class Host;
|
||||||
|
class AbstractClientData // Abstract base class for attached data
|
||||||
|
{
|
||||||
|
virtual ~AbstractClientData(); // minimum for memory management
|
||||||
|
|
||||||
class Host;
|
// optional extra observer protocols
|
||||||
class AbstractClientData // Abstract base class for attached data
|
virtual void NotificationMethod(
|
||||||
|
// maybe host passes reference to self, maybe not
|
||||||
|
// Host &host
|
||||||
|
) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Host
|
||||||
|
: public ClientData::Site< Host, AbstractClientData >
|
||||||
|
// That inheritance is a case of CRTP
|
||||||
|
// (the "curiously recurring template pattern")
|
||||||
|
// in which the base class takes the derived class as a template argument
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Host()
|
||||||
{
|
{
|
||||||
virtual ~AbstractClientData(); // minimum for memory management
|
// If using an Observer protocol, force early creation of all client
|
||||||
|
// data:
|
||||||
// optional extra observer protocols
|
BuildAll();
|
||||||
virtual void NotificationMethod(
|
|
||||||
// maybe host passes reference to self, maybe not
|
|
||||||
// Host &host
|
|
||||||
) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Host
|
|
||||||
: public ClientData::Site< Host, AbstractClientData >
|
|
||||||
// That inheritance is a case of CRTP
|
|
||||||
// (the "curiously recurring template pattern")
|
|
||||||
// in which the base class takes the derived class as a template argument
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Host()
|
|
||||||
{
|
|
||||||
// If using an Observer protocol, force early creation of all client
|
|
||||||
// data:
|
|
||||||
BuildAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifyAll()
|
|
||||||
{
|
|
||||||
// Visit all non-null objects
|
|
||||||
ForEach( []( AbstractClientData &data ){
|
|
||||||
data.NotificationMethod(
|
|
||||||
// *this
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typical usage pattern in client module -- observer pattern, and retrieval
|
void NotifyAll()
|
||||||
|
{
|
||||||
|
// Visit all non-null objects
|
||||||
|
ForEach( []( AbstractClientData &data ){
|
||||||
|
data.NotificationMethod(
|
||||||
|
// *this
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
@par Client side usage pattern
|
||||||
|
|
||||||
class MyClientData : public AbstractClientData
|
```
|
||||||
|
class MyClientData : public AbstractClientData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyClientData( Host &host )
|
||||||
{
|
{
|
||||||
public:
|
// ... use host, maybe keep a back pointer to it, maybe not,
|
||||||
MyClientData( Host &host )
|
// depending how Host declares NotificationMethod ...
|
||||||
{
|
// ... Maybe Host too is an abstract class and we invoke some
|
||||||
// ... use host, maybe keep a back pointer to it, maybe not,
|
// virtual function of it ...
|
||||||
// depending how Host declares NotificationMethod ...
|
}
|
||||||
// ... Maybe Host too is an abstract class and we invoke some
|
void NotificationMethod(
|
||||||
// virtual function of it ...
|
// Host &host
|
||||||
}
|
) override
|
||||||
void NotificationMethod(
|
|
||||||
// Host &host
|
|
||||||
) override
|
|
||||||
{
|
|
||||||
// ... Observer actions
|
|
||||||
// (If there is more than one separately compiled module using this
|
|
||||||
// protocol, beware that the sequence of notifications is unspecified)
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int mExtraStuff;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Registration of a factory at static initialization time, to be called
|
|
||||||
// when a Host uses BuildAll, or else lazily when client code uses
|
|
||||||
// Host::Get()
|
|
||||||
static const Host::RegisteredFactory key{
|
|
||||||
[]( Host &host ){ return std::make_unique< MyClientData >( host ); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use of that key at other times, not dependent on notifications from
|
|
||||||
// the core
|
|
||||||
void DoSomething( Host &host )
|
|
||||||
{
|
{
|
||||||
// This may force lazy construction of ClientData, always returning
|
// ... Observer actions
|
||||||
// an object (or else throwing)
|
// (If there is more than one separately compiled module using this
|
||||||
auto &data = host.Get< MyClientData >( key );
|
// protocol, beware that the sequence of notifications is unspecified)
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int mExtraStuff;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Registration of a factory at static initialization time, to be called
|
||||||
|
// when a Host uses BuildAll, or else lazily when client code uses
|
||||||
|
// Host::Get()
|
||||||
|
static const Host::RegisteredFactory key{
|
||||||
|
[]( Host &host ){ return std::make_unique< MyClientData >( host ); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use of that key at other times, not dependent on notifications from
|
||||||
|
// the core
|
||||||
|
void DoSomething( Host &host )
|
||||||
|
{
|
||||||
|
// This may force lazy construction of MyClientData, always returning
|
||||||
|
// an object (or else throwing)
|
||||||
|
auto &data = host.Get< MyClientData >( key );
|
||||||
|
auto val = pData->mExtraStuff;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoAnotherThing( Host &host )
|
||||||
|
{
|
||||||
|
// Does not force lazy construction of MyClientData
|
||||||
|
auto *pData = host.Find< MyClientData >( key );
|
||||||
|
if ( pData ) {
|
||||||
auto val = data.mExtraStuff;
|
auto val = data.mExtraStuff;
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DoAnotherThing( Host &host )
|
void DoYetAnotherThing( Host &host )
|
||||||
{
|
{
|
||||||
// Does not force lazy construction of ClientData
|
// Reassign the pointer in this client's slot
|
||||||
auto *pData = host.Find< MyClientData >( key );
|
host.Assign( key, MyReplacementObject( host ) );
|
||||||
if ( pData ) {
|
}
|
||||||
auto val = data.mExtraStuff;
|
```
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoYetAnotherThing( Host &host )
|
@par Lazy or eager construction
|
||||||
{
|
|
||||||
// Reassign the pointer in this client's slot
|
|
||||||
host.Assign( key, MyReplacementObject( host ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
About laziness: If the client only needs retrieval, it might need
|
If the client only needs retrieval, it might need
|
||||||
construction of data only on demand. But if the host is meant to push
|
construction of data only on demand. But if the host is meant to push
|
||||||
notifications to the clients, then the host class is responsible for forcing
|
notifications to the clients, then the host class is responsible for forcing
|
||||||
early building of all ClientData when host is constructed, as in the example
|
early building of all ClientData when host is constructed, as in the example
|
||||||
above.
|
above.
|
||||||
|
|
||||||
About unusual registration sequences: if registration of a factory
|
@par Unusual registration sequences
|
||||||
|
|
||||||
|
If registration of a factory
|
||||||
happens after some host objects are already in existence, their associated
|
happens after some host objects are already in existence, their associated
|
||||||
client data fail to be created if you rely only on BuildAll in the Host
|
client data fail to be created if you rely only on BuildAll in the @B Host
|
||||||
constructor. Early deregistration of factories is permitted, in which case
|
constructor. Early deregistration of factories is permitted, in which case
|
||||||
any later constructed host objects will carry null pointers in the associated
|
any later constructed host objects will carry null pointers in the associated
|
||||||
slot, and a small "leak" in the space of per-host slots will persist until
|
slot, and a small "leak" in the space of per-host slots will persist until
|
||||||
the program exits. All such usage is not recommended.
|
the program exits. All such usage is not recommended.
|
||||||
*/
|
|
||||||
|
@tparam Host
|
||||||
|
Type that derives from this base class; it
|
||||||
|
supports hooks to invoke attached object factories. This is an example of the
|
||||||
|
[curiously recurring template pattern](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern#General_form)
|
||||||
|
|
||||||
|
@tparam ClientData Common base class of attachments; must have a virtual destructor
|
||||||
|
|
||||||
|
@tparam CopyingPolicy @ref CopyingPolicy value Chooses deep, shallow, or (default) no-op copying of attachments
|
||||||
|
|
||||||
|
@tparam Pointer
|
||||||
|
The kind of pointer @b Host will hold to ClientData; default is std::unique_ptr.
|
||||||
|
You might want to use std::shared_ptr, std::weak_ptr, or wxWeakRef instead
|
||||||
|
|
||||||
|
@tparam ObjectLockingPolicy @ref LockingPolicy value chooses thread safety policy for array of attachments in each @b Host, default is unsafe
|
||||||
|
|
||||||
|
@tparam RegistryLockingPolicy @ref LockingPolicy value chooses thread safety policy for the static table of attachment factory functions, default is unsafe
|
||||||
|
*/
|
||||||
template<
|
template<
|
||||||
typename Host,
|
typename Host,
|
||||||
typename ClientData = Base,
|
typename ClientData = Base,
|
||||||
@ -180,12 +211,8 @@ template<
|
|||||||
|
|
||||||
CopyingPolicy ObjectCopyingPolicy = SkipCopying,
|
CopyingPolicy ObjectCopyingPolicy = SkipCopying,
|
||||||
|
|
||||||
// The kind of pointer Host will hold to ClientData; you might want to
|
|
||||||
// use std::shared_ptr, std::weak_ptr, or wxWeakRef instead
|
|
||||||
template<typename> class Pointer = UniquePtr,
|
template<typename> class Pointer = UniquePtr,
|
||||||
|
|
||||||
// Thread safety policies for the tables of ClientData objects in each Host
|
|
||||||
// object, and for the static factory function registry
|
|
||||||
LockingPolicy ObjectLockingPolicy = NoLocking,
|
LockingPolicy ObjectLockingPolicy = NoLocking,
|
||||||
LockingPolicy RegistryLockingPolicy = NoLocking
|
LockingPolicy RegistryLockingPolicy = NoLocking
|
||||||
>
|
>
|
||||||
@ -198,9 +225,9 @@ public:
|
|||||||
"ClientData::Site requires a data class with a virtual destructor" );
|
"ClientData::Site requires a data class with a virtual destructor" );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Associated type aliases
|
|
||||||
using DataType = ClientData;
|
using DataType = ClientData;
|
||||||
using DataPointer = Pointer< ClientData >;
|
using DataPointer = Pointer< ClientData >;
|
||||||
|
//! Type of function from which RegisteredFactory is constructed; it builds attachments
|
||||||
using DataFactory = std::function< DataPointer( Host& ) >;
|
using DataFactory = std::function< DataPointer( Host& ) >;
|
||||||
|
|
||||||
Site()
|
Site()
|
||||||
@ -220,19 +247,27 @@ public:
|
|||||||
Site& operator =( Site && other )
|
Site& operator =( Site && other )
|
||||||
{ mData = std::move(other.mData); return *this; }
|
{ mData = std::move(other.mData); return *this; }
|
||||||
|
|
||||||
|
//! How many attachment pointers are in the Site
|
||||||
size_t size() const { return mData.size(); }
|
size_t size() const { return mData.size(); }
|
||||||
|
|
||||||
|
//! How many static factories have been registered with this specialization of Site
|
||||||
|
/*!
|
||||||
|
Usually agrees with the size() of each site unless some registrations happened later
|
||||||
|
than some Site's construction.
|
||||||
|
*/
|
||||||
static size_t slots() { return GetFactories().mObject.size(); }
|
static size_t slots() { return GetFactories().mObject.size(); }
|
||||||
|
|
||||||
/// \brief a type meant to be stored by client code in a static variable,
|
//! Client code makes static instance from a factory of attachments; passes it to @ref Get or @ref Find as a retrieval key
|
||||||
/// and used as a retrieval key to get the manufactured client object back
|
/*!
|
||||||
/// from the host object.
|
It can be destroyed to de-register the factory, but usually not before
|
||||||
/// It can be destroyed to de-register the factory, but usually not before
|
destruction of statics at program exit.
|
||||||
/// destruction of statics at program exit.
|
*/
|
||||||
class RegisteredFactory
|
class RegisteredFactory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RegisteredFactory( DataFactory factory )
|
RegisteredFactory(
|
||||||
|
DataFactory factory
|
||||||
|
)
|
||||||
{
|
{
|
||||||
auto factories = GetFactories();
|
auto factories = GetFactories();
|
||||||
mIndex = factories.mObject.size();
|
mIndex = factories.mObject.size();
|
||||||
@ -259,12 +294,17 @@ public:
|
|||||||
size_t mIndex;
|
size_t mIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
// member functions for use by clients
|
//! @name Retrieval and reassignment of attachments
|
||||||
|
//! @{
|
||||||
|
|
||||||
// \brief Get a reference to an object, creating it on demand if needed, and
|
//! Get reference to an attachment, creating on demand if not present, down-cast it to @b Subclass
|
||||||
// down-cast it with static_cast. Throws on failure to create it.
|
/*!
|
||||||
// (Lifetime of the object may depend on the Host's lifetime and also on the
|
Uses static_cast. Throws on failure to create it.
|
||||||
// client's use of Assign(). Site is not responsible for guarantees.)
|
(Lifetime of the object may depend on the host's lifetime and also on the
|
||||||
|
client's use of Assign(). Site is not responsible for guarantees.)
|
||||||
|
@tparam Subclass Expected actual type of attachment, assumed to be correct
|
||||||
|
@param key Reference to static object created in client code
|
||||||
|
*/
|
||||||
template< typename Subclass = ClientData >
|
template< typename Subclass = ClientData >
|
||||||
Subclass &Get( const RegisteredFactory &key )
|
Subclass &Get( const RegisteredFactory &key )
|
||||||
{
|
{
|
||||||
@ -272,7 +312,8 @@ public:
|
|||||||
return DoGet< Subclass >( data, key );
|
return DoGet< Subclass >( data, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
// const counterpart of the previous
|
//! @copydoc Get
|
||||||
|
/*! const overload returns const references only. */
|
||||||
template< typename Subclass = const ClientData >
|
template< typename Subclass = const ClientData >
|
||||||
auto Get( const RegisteredFactory &key ) const -> typename
|
auto Get( const RegisteredFactory &key ) const -> typename
|
||||||
std::enable_if< std::is_const< Subclass >::value, Subclass & >::type
|
std::enable_if< std::is_const< Subclass >::value, Subclass & >::type
|
||||||
@ -281,10 +322,13 @@ public:
|
|||||||
return DoGet< Subclass >( data, key );
|
return DoGet< Subclass >( data, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
// \brief Get a (bare) pointer to an object, or null, and down-cast it with
|
//!Get a (bare) pointer to an attachment, or null, down-cast it to @b Subclass *; will not create on demand
|
||||||
// static_cast. Do not create any object.
|
/*!
|
||||||
// (Lifetime of the object may depend on the Host's lifetime and also on the
|
(Lifetime of the object may depend on the host's lifetime and also on the
|
||||||
// client's use of Assign(). Site is not responsible for guarantees.)
|
client's use of Assign(). Site is not responsible for guarantees.)
|
||||||
|
@tparam Subclass Expected actual type of attachment, assumed to be correct
|
||||||
|
@param key Reference to static object created in client code
|
||||||
|
*/
|
||||||
template< typename Subclass = ClientData >
|
template< typename Subclass = ClientData >
|
||||||
Subclass *Find( const RegisteredFactory &key )
|
Subclass *Find( const RegisteredFactory &key )
|
||||||
{
|
{
|
||||||
@ -292,7 +336,8 @@ public:
|
|||||||
return DoFind< Subclass >( data, key );
|
return DoFind< Subclass >( data, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
// const counterpart of the previous
|
//! @copydoc Find
|
||||||
|
/*! const overload returns pointers to const only. */
|
||||||
template< typename Subclass = const ClientData >
|
template< typename Subclass = const ClientData >
|
||||||
auto Find( const RegisteredFactory &key ) const -> typename
|
auto Find( const RegisteredFactory &key ) const -> typename
|
||||||
std::enable_if< std::is_const< Subclass >::value, Subclass * >::type
|
std::enable_if< std::is_const< Subclass >::value, Subclass * >::type
|
||||||
@ -301,11 +346,16 @@ public:
|
|||||||
return DoFind< Subclass >( data, key );
|
return DoFind< Subclass >( data, key );
|
||||||
}
|
}
|
||||||
|
|
||||||
// \brief Reassign Host's pointer to ClientData.
|
//! Reassign Site's pointer to ClientData.
|
||||||
// If there is object locking, then reassignments to the same slot in the
|
/*!
|
||||||
// same host object are serialized.
|
If @b ObjectLockingPolicy isn't default, then reassignments are serialized.
|
||||||
|
@tparam ReplacementPointer @b Pointer<Subclass> where @b Subclass derives ClientData
|
||||||
|
*/
|
||||||
template< typename ReplacementPointer >
|
template< typename ReplacementPointer >
|
||||||
void Assign( const RegisteredFactory &key, ReplacementPointer &&replacement )
|
void Assign(
|
||||||
|
const RegisteredFactory &key, //!< Reference to static object created in client code
|
||||||
|
ReplacementPointer &&replacement //!< A temporary or std::move'd pointer
|
||||||
|
)
|
||||||
{
|
{
|
||||||
auto index = key.mIndex;
|
auto index = key.mIndex;
|
||||||
auto data = GetData();
|
auto data = GetData();
|
||||||
@ -314,12 +364,18 @@ public:
|
|||||||
// Copy or move as appropriate:
|
// Copy or move as appropriate:
|
||||||
*iter = std::forward< ReplacementPointer >( replacement );
|
*iter = std::forward< ReplacementPointer >( replacement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// member functions for use by Host
|
//! @name member functions for use by @b Host
|
||||||
|
//! @{
|
||||||
|
|
||||||
// \brief Invoke function on each ClientData object that has been created in
|
//! Invoke function on each ClientData object that has been created in @c this
|
||||||
// this, but do not cause the creation of any.
|
/*!
|
||||||
|
@tparam Function takes reference to ClientData, return value is ignored
|
||||||
|
@param function of type @b Function
|
||||||
|
*/
|
||||||
template< typename Function >
|
template< typename Function >
|
||||||
void ForEach( const Function &function )
|
void ForEach( const Function &function )
|
||||||
{
|
{
|
||||||
@ -331,8 +387,8 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const counterpart of previous, only compiles with a function that takes
|
//! @copydoc ForEach
|
||||||
// a value or const reference argument
|
/*! const overload only compiles with a function that takes reference to const ClientData. */
|
||||||
template< typename Function >
|
template< typename Function >
|
||||||
void ForEach( const Function &function ) const
|
void ForEach( const Function &function ) const
|
||||||
{
|
{
|
||||||
@ -346,11 +402,12 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// \brief Invoke predicate on the ClientData objects that have been created in
|
//! Return pointer to first attachment in @c this that is not null and satisfies a predicate, or nullptr
|
||||||
// this, but do not cause the creation of any. Stop at the first for which
|
/*!
|
||||||
// the predicate returns true, and return a pointer to the corresponding
|
Beware that the sequence of visitation is not specified.
|
||||||
// object, or return nullptr if no return values were true.
|
@tparam Function takes reference to ClientData, returns value convertible to bool
|
||||||
// Beware that the sequence of visitation is not specified.
|
@param function of type @b Function
|
||||||
|
*/
|
||||||
template< typename Function >
|
template< typename Function >
|
||||||
ClientData *FindIf( const Function &function )
|
ClientData *FindIf( const Function &function )
|
||||||
{
|
{
|
||||||
@ -363,8 +420,8 @@ protected:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const counterpart of previous, only compiles with a function that takes
|
//! @copydoc FindIf
|
||||||
// a value or const reference argument
|
/*! const overload only compiles with a function callable with a const reference to ClientData. */
|
||||||
template< typename Function >
|
template< typename Function >
|
||||||
const ClientData *FindIf( const Function &function ) const
|
const ClientData *FindIf( const Function &function ) const
|
||||||
{
|
{
|
||||||
@ -380,8 +437,7 @@ protected:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// \brief For each registered factory, if the corresponding object in this
|
//! For each RegisteredFactory, if the corresponding attachment is absent in @c this, build and store it
|
||||||
// is absent, then invoke the factory and store the result.
|
|
||||||
void BuildAll()
|
void BuildAll()
|
||||||
{
|
{
|
||||||
// Note that we cannot call this function in the Site constructor as we
|
// Note that we cannot call this function in the Site constructor as we
|
||||||
@ -402,6 +458,8 @@ protected:
|
|||||||
static_cast< void >( Build( data, iter, ii ) );
|
static_cast< void >( Build( data, iter, ii ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using DataFactories =
|
using DataFactories =
|
||||||
Lockable< std::vector< DataFactory >, RegistryLockingPolicy >;
|
Lockable< std::vector< DataFactory >, RegistryLockingPolicy >;
|
||||||
@ -500,9 +558,8 @@ private:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container of pointers returned by factories, per instance of Host class
|
//! Container of pointers returned by factories, per instance of @b Host class
|
||||||
// This is the only non-static data member that Site injects into the
|
/*! This is the only non-static data member that Site injects into the derived class. */
|
||||||
// derived class.
|
|
||||||
DataContainer mData;
|
DataContainer mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
/**********************************************************************
|
/*!********************************************************************
|
||||||
|
|
||||||
Audacity: A Digital Audio Editor
|
Audacity: A Digital Audio Editor
|
||||||
|
|
||||||
ClientDataHelpers.h
|
@file ClientDataHelpers.h
|
||||||
|
@brief Some implementation details for ClientData
|
||||||
|
|
||||||
Paul Licameli
|
Paul Licameli
|
||||||
|
|
||||||
@ -18,21 +19,21 @@ Paul Licameli
|
|||||||
namespace ClientData {
|
namespace ClientData {
|
||||||
// Helpers to define ClientData::Site class template
|
// Helpers to define ClientData::Site class template
|
||||||
|
|
||||||
// To specify (separately for the table of factories, and for the per-Site
|
//! Statically specify whether there is mutual exclusion (separately for the table of factories, and for the per-host container of client objects).
|
||||||
// container of client data objects) whether to ensure mutual exclusion.
|
/*! Used as non-type template parameter of ClientData::Site */
|
||||||
enum LockingPolicy {
|
enum LockingPolicy {
|
||||||
NoLocking,
|
NoLocking,
|
||||||
NonrecursiveLocking, // using std::mutex
|
NonrecursiveLocking, //!< using std::mutex
|
||||||
RecursiveLocking, // using std::recursive_mutex
|
RecursiveLocking, //!< using std::recursive_mutex
|
||||||
};
|
};
|
||||||
|
|
||||||
// To specify how the Site implements its copy constructor and assignment.
|
//! Statically specify how the ClientData::Site implements its copy constructor and assignment.
|
||||||
// (Move construction and assignment always work.)
|
/*! (Move construction and assignment always work.)
|
||||||
|
Used as non-type template parameter of ClientData::Site */
|
||||||
enum CopyingPolicy {
|
enum CopyingPolicy {
|
||||||
SkipCopying, // copy ignores the argument and constructs empty
|
SkipCopying, //!< ignore the source and leave empty
|
||||||
ShallowCopying, // just copy smart pointers; won't compile for unique_ptr
|
ShallowCopying, //!< copy pointers only; won't compile for std::unique_ptr
|
||||||
DeepCopying, // requires ClientData to define a Clone() member;
|
DeepCopying, //!< point to new sub-objects; these must define a Clone() member; won't compile for std::weak_ptr
|
||||||
// won't compile for weak_ptr (and wouldn't work)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
@ -41,40 +42,45 @@ template<
|
|||||||
template<typename> class Owner
|
template<typename> class Owner
|
||||||
> struct Cloneable;
|
> struct Cloneable;
|
||||||
|
|
||||||
// A conversion so we can use operator * in all the likely cases for the
|
//! Conversion allowing operator * on any @b Pointer parameter of ClientData::Site
|
||||||
// template parameter Pointer. (Return value should be bound only to const
|
/*! Return value should be bound to a const reference */
|
||||||
// lvalue references)
|
|
||||||
template< typename Ptr > static inline
|
template< typename Ptr > static inline
|
||||||
const Ptr &Dereferenceable( Ptr &p )
|
const Ptr &Dereferenceable( Ptr &p )
|
||||||
{ return p; } // returns an lvalue
|
{ return p; }
|
||||||
|
//! Overload of ClientData::Dereferenceable returns an rvalue
|
||||||
template< typename Obj > static inline
|
template< typename Obj > static inline
|
||||||
std::shared_ptr<Obj> Dereferenceable( std::weak_ptr<Obj> &p )
|
std::shared_ptr<Obj> Dereferenceable( std::weak_ptr<Obj> &p )
|
||||||
{ return p.lock(); } // overload returns a prvalue
|
{ return p.lock(); }
|
||||||
|
|
||||||
// Decorator template to implement locking policies
|
//! Decorator template injects type Lock and method lock() into interface of @b Object
|
||||||
template< typename Object, LockingPolicy > struct Lockable;
|
/*!
|
||||||
|
@tparam Object decorated class
|
||||||
|
@tparam LockingPolicy one of ClientData::LockingPolicy
|
||||||
|
*/
|
||||||
|
template< typename Object, LockingPolicy > struct Lockable{};
|
||||||
|
//! Specialization for trivial, non-locking policy
|
||||||
template< typename Object > struct Lockable< Object, NoLocking >
|
template< typename Object > struct Lockable< Object, NoLocking >
|
||||||
: Object {
|
: Object {
|
||||||
// implement trivial non-locking policy
|
//! Empty class
|
||||||
struct Lock{};
|
struct Lock{};
|
||||||
Lock lock() const { return {}; }
|
Lock lock() const { return {}; }
|
||||||
};
|
};
|
||||||
|
//! Specialization for real locking with std::mutex
|
||||||
template< typename Object > struct Lockable< Object, NonrecursiveLocking >
|
template< typename Object > struct Lockable< Object, NonrecursiveLocking >
|
||||||
: Object, std::mutex {
|
: Object, std::mutex {
|
||||||
// implement real locking
|
|
||||||
using Lock = std::unique_lock< std::mutex >;
|
using Lock = std::unique_lock< std::mutex >;
|
||||||
Lock lock() const { return Lock{ *this }; }
|
Lock lock() const { return Lock{ *this }; }
|
||||||
};
|
};
|
||||||
|
//! Specialization for real locking with std::recursive_mutex
|
||||||
template< typename Object > struct Lockable< Object, RecursiveLocking >
|
template< typename Object > struct Lockable< Object, RecursiveLocking >
|
||||||
: Object, std::recursive_mutex {
|
: Object, std::recursive_mutex {
|
||||||
// implement real locking
|
|
||||||
using Lock = std::unique_lock< std::recursive_mutex >;
|
using Lock = std::unique_lock< std::recursive_mutex >;
|
||||||
Lock lock() const { return Lock{ *this }; }
|
Lock lock() const { return Lock{ *this }; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pairing of a reference to a Lockable and a lock on it
|
//! Decorated reference to a ClientData::Lockable, with a current lock on it
|
||||||
|
/*! Uses inheritance to benefit from the empty base class optimization if possible */
|
||||||
template< typename Lockable > struct Locked
|
template< typename Lockable > struct Locked
|
||||||
// inherit, maybe empty base class optimization applies:
|
|
||||||
: private Lockable::Lock
|
: private Lockable::Lock
|
||||||
{
|
{
|
||||||
explicit Locked( Lockable &object )
|
explicit Locked( Lockable &object )
|
||||||
@ -84,8 +90,9 @@ template< typename Lockable > struct Locked
|
|||||||
Lockable &mObject;
|
Lockable &mObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Decorator template implements the copying policy
|
//! Decorator template injects copy and move operators for container of pointers
|
||||||
template< typename Container, CopyingPolicy > struct Copyable;
|
template< typename Container, CopyingPolicy > struct Copyable{};
|
||||||
|
//! Specialization that ignores contents of the source when copying (not when moving).
|
||||||
template< typename Container > struct Copyable< Container, SkipCopying >
|
template< typename Container > struct Copyable< Container, SkipCopying >
|
||||||
: Container {
|
: Container {
|
||||||
Copyable() = default;
|
Copyable() = default;
|
||||||
@ -94,11 +101,14 @@ template< typename Container > struct Copyable< Container, SkipCopying >
|
|||||||
Copyable( Copyable && ) = default;
|
Copyable( Copyable && ) = default;
|
||||||
Copyable &operator=( Copyable&& ) = default;
|
Copyable &operator=( Copyable&& ) = default;
|
||||||
};
|
};
|
||||||
|
//! Specialization that copies pointers, not sub-objects; [strong guarantee](@ref Strong-guarantee) for assignment
|
||||||
template< typename Container > struct Copyable< Container, ShallowCopying >
|
template< typename Container > struct Copyable< Container, ShallowCopying >
|
||||||
: Container {
|
: Container {
|
||||||
Copyable() = default;
|
Copyable() = default;
|
||||||
|
//! Call through to operator =
|
||||||
Copyable( const Copyable &other )
|
Copyable( const Copyable &other )
|
||||||
{ *this = other; }
|
{ *this = other; }
|
||||||
|
//! @excsafety{Strong}
|
||||||
Copyable &operator=( const Copyable &other )
|
Copyable &operator=( const Copyable &other )
|
||||||
{
|
{
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
@ -113,11 +123,14 @@ template< typename Container > struct Copyable< Container, ShallowCopying >
|
|||||||
Copyable( Copyable && ) = default;
|
Copyable( Copyable && ) = default;
|
||||||
Copyable &operator=( Copyable&& ) = default;
|
Copyable &operator=( Copyable&& ) = default;
|
||||||
};
|
};
|
||||||
|
//! Specialization that clones sub-objects when copying; [strong guarantee](@ref Strong-guarantee) for assignment
|
||||||
template< typename Container > struct Copyable< Container, DeepCopying >
|
template< typename Container > struct Copyable< Container, DeepCopying >
|
||||||
: Container {
|
: Container {
|
||||||
Copyable() = default;
|
Copyable() = default;
|
||||||
|
//! Call through to operator =
|
||||||
Copyable( const Copyable &other )
|
Copyable( const Copyable &other )
|
||||||
{ *this = other; }
|
{ *this = other; }
|
||||||
|
//! @excsafety{Strong}
|
||||||
Copyable &operator=( const Copyable &other )
|
Copyable &operator=( const Copyable &other )
|
||||||
{
|
{
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user