1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-04 14:19:30 +02:00

Much Doxygen commentary for Track.h...

... Comments and indentation changes only, with a small bit of code movement to
improve the sequence of items on the Doxygen page.

Comments some obscurities of template usage and of iterators (TrackIter and
TrackNodePointer).
This commit is contained in:
Paul Licameli 2020-08-23 07:51:01 -04:00
parent 372393f49e
commit d420fdecf4
2 changed files with 229 additions and 156 deletions

View File

@ -9,7 +9,7 @@
*******************************************************************//** *******************************************************************//**
\class Track \class Track
\brief Fundamental data object of Audacity, placed in the TrackPanel. \brief Fundamental data object of Audacity, displayed in the TrackPanel.
Classes derived form it include the WaveTrack, NoteTrack, LabelTrack Classes derived form it include the WaveTrack, NoteTrack, LabelTrack
and TimeTrack. and TimeTrack.
@ -800,8 +800,9 @@ TrackNodePointer TrackList::Remove(Track *t)
void TrackList::Clear(bool sendEvent) void TrackList::Clear(bool sendEvent)
{ {
// Null out the back-pointers in tracks, in case there are outstanding // Null out the back-pointers to this in tracks, in case there
// shared_ptrs to those tracks. // are outstanding shared_ptrs to those tracks, making them outlive
// the temporary ListOfTracks below.
for ( auto pTrack: *this ) for ( auto pTrack: *this )
pTrack->SetOwner( {}, {} ); pTrack->SetOwner( {}, {} );
for ( auto pTrack: mPendingUpdates ) for ( auto pTrack: mPendingUpdates )

View File

@ -1,8 +1,9 @@
/********************************************************************** /*!********************************************************************
Audacity: A Digital Audio Editor Audacity: A Digital Audio Editor
Track.h @file Track.h
@brief declares abstract base class Track, TrackList, and iterators over TrackList
Dominic Mazzoni Dominic Mazzoni
@ -50,6 +51,9 @@ class TrackList;
using ListOfTracks = std::list< std::shared_ptr< Track > >; using ListOfTracks = std::list< std::shared_ptr< Track > >;
//! Pairs a std::list iterator and a pointer to a list, for comparison purposes
/*! Compare owning lists first, and only if same, then the iterators;
else MSVC debug runtime complains. */
using TrackNodePointer = using TrackNodePointer =
std::pair< ListOfTracks::iterator, ListOfTracks* >; std::pair< ListOfTracks::iterator, ListOfTracks* >;
@ -59,21 +63,22 @@ inline bool operator == (const TrackNodePointer &a, const TrackNodePointer &b)
inline bool operator != (const TrackNodePointer &a, const TrackNodePointer &b) inline bool operator != (const TrackNodePointer &a, const TrackNodePointer &b)
{ return !(a == b); } { return !(a == b); }
//! Enumerates all subclasses of Track (not just the leaf classes) and the None value
enum class TrackKind enum class TrackKind
{ {
None, None, //!< no class
Wave, Wave,
Note, Note,
Label, Label,
Time, Time,
Audio, Audio, //!< nonleaf
Playable, Playable, //!< nonleaf
All All //!< the root class
}; };
/// Compile-time function on enum values. //! Compile-time function on enum values
/// It knows all inheritance relations among Track subclasses /*! It knows all inheritance relations among Track subclasses
/// even where the track types are only forward declared. even where the track types are only forward declared. */
constexpr bool CompatibleTrackKinds( TrackKind desired, TrackKind actual ) constexpr bool CompatibleTrackKinds( TrackKind desired, TrackKind actual )
{ {
return return
@ -95,10 +100,10 @@ constexpr bool CompatibleTrackKinds( TrackKind desired, TrackKind actual )
; ;
} }
/// \brief Metaprogramming in TrackTyper lets track_cast work even when the track //! Metaprogramming enabling track_cast even when the subclasses are incomplete types
/// subclasses are visible only as incomplete types
namespace TrackTyper { namespace TrackTyper {
template<typename, TrackKind> struct Pair; template<typename, TrackKind> struct Pair;
//! Compile-time map from types to enum values
using List = std::tuple< using List = std::tuple<
Pair<Track, TrackKind::All>, Pair<Track, TrackKind::All>,
Pair<AudioTrack, TrackKind::Audio>, Pair<AudioTrack, TrackKind::Audio>,
@ -109,13 +114,16 @@ namespace TrackTyper {
Pair<WaveTrack, TrackKind::Wave> Pair<WaveTrack, TrackKind::Wave>
// New classes can be added easily to this list // New classes can be added easily to this list
>; >;
template<typename...> struct Lookup; //! Variadic template implements metafunction with specializations, to associate enum values with types
template<typename...> struct Lookup {};
//! Base case of metafunction
template<typename TrackType, TrackKind Here, typename... Rest> template<typename TrackType, TrackKind Here, typename... Rest>
struct Lookup< TrackType, std::tuple< Pair<TrackType, Here>, Rest... > > { struct Lookup< TrackType, std::tuple< Pair<TrackType, Here>, Rest... > > {
static constexpr TrackKind value() { static constexpr TrackKind value() {
return Here; return Here;
} }
}; };
//! Recursive case of metafunction
template<typename TrackType, typename NotHere, typename... Rest> template<typename TrackType, typename NotHere, typename... Rest>
struct Lookup< TrackType, std::tuple< NotHere, Rest... > > { struct Lookup< TrackType, std::tuple< NotHere, Rest... > > {
static constexpr TrackKind value() { static constexpr TrackKind value() {
@ -124,6 +132,7 @@ namespace TrackTyper {
}; };
}; };
//! Metafunction from track subtype (as template parameter) to enum value
template<typename TrackType> constexpr TrackKind track_kind () template<typename TrackType> constexpr TrackKind track_kind ()
{ {
using namespace TrackTyper; using namespace TrackTyper;
@ -143,15 +152,14 @@ template<typename T>
>::type >::type
track_cast(const Track *track); track_cast(const Track *track);
class ViewInfo; //! An in-session identifier of track objects across undo states. It does not persist between sessions
/*!
/// This is an in-session identifier of track objects across undo states Default constructed value is not equal to the id of any track that has ever
/// been added to a TrackList, or (directly or transitively) copied from such.
/// It does not persist between sessions (A track added by TrackList::RegisterPendingNewTrack() that is not yet applied is not
/// Default constructed value is not equal to the id of any track that has ever considered added.)
/// been added to a TrackList, or (directly or transitively) copied from such
/// (A pending additional track that is not yet applied is not considered added) TrackIds are assigned uniquely across projects. */
/// TrackIds are assigned uniquely across projects
class TrackId class TrackId
{ {
public: public:
@ -173,10 +181,12 @@ private:
long mValue; long mValue;
}; };
//! Template generated base class for Track lets it host opaque UI related objects
using AttachedTrackObjects = ClientData::Site< using AttachedTrackObjects = ClientData::Site<
Track, ClientData::Base, ClientData::SkipCopying, std::shared_ptr Track, ClientData::Base, ClientData::SkipCopying, std::shared_ptr
>; >;
//! Abstract base class for an object holding data associated with points on a time axis
class AUDACITY_DLL_API Track /* not final */ class AUDACITY_DLL_API Track /* not final */
: public XMLTagHandler : public XMLTagHandler
, public AttachedTrackObjects , public AttachedTrackObjects
@ -184,14 +194,15 @@ class AUDACITY_DLL_API Track /* not final */
{ {
friend class TrackList; friend class TrackList;
// To be TrackDisplay
private: private:
TrackId mId; TrackId mId; //!< Identifies the track only in-session, not persistently
protected: protected:
std::weak_ptr<TrackList> mList; std::weak_ptr<TrackList> mList; //!< Back pointer to owning TrackList
//! Holds iterator to self, so that TrackList::Find can be constant-time
/*! mNode's pointer to std::list might not be this TrackList, if it's a pending update track */
TrackNodePointer mNode{}; TrackNodePointer mNode{};
int mIndex; int mIndex; //!< 0-based position of this track in its TrackList
wxString mName; wxString mName;
wxString mDefaultName; wxString mDefaultName;
@ -203,6 +214,7 @@ class AUDACITY_DLL_API Track /* not final */
public: public:
//! Alias for my base type
using AttachedObjects = ::AttachedTrackObjects; using AttachedObjects = ::AttachedTrackObjects;
using ChannelType = XMLValueChecker::ChannelType; using ChannelType = XMLValueChecker::ChannelType;
@ -296,7 +308,9 @@ private:
// No need yet to make this virtual // No need yet to make this virtual
void DoSetLinked(bool l); void DoSetLinked(bool l);
//! Retrieve mNode with debug checks
TrackNodePointer GetNode() const; TrackNodePointer GetNode() const;
//! Update mNode when Track is added to TrackList, or removed from it
void SetOwner void SetOwner
(const std::weak_ptr<TrackList> &list, TrackNodePointer node); (const std::weak_ptr<TrackList> &list, TrackNodePointer node);
@ -397,79 +411,63 @@ public:
bool SameKindAs(const Track &track) const bool SameKindAs(const Track &track) const
{ return GetKind() == track.GetKind(); } { return GetKind() == track.GetKind(); }
//! Type of arguments passed as optional second parameter to TypeSwitch() cases
template < typename R = void > template < typename R = void >
using Continuation = std::function< R() >; using Continuation = std::function< R() >;
//! Type of arguments passed as optional second parameter to TypeSwitch<void>() cases
using Fallthrough = Continuation<>; using Fallthrough = Continuation<>;
private: private:
//! Variadic template implements metafunction with specializations, to dispatch Track::TypeSwitch
// Variadic template specialized below
template< typename ...Params > template< typename ...Params >
struct Executor; struct Executor{};
// This specialization grounds the recursion. //! Helper for recursive case of metafunction implementing Track::TypeSwitch
template< typename R, typename ConcreteType > /*! Mutually recursive (in compile time) with tempate Track::Executor. */
struct Executor< R, ConcreteType >
{
enum : unsigned { SetUsed = 0 };
// No functions matched, so do nothing.
R operator () (const void *) { return R{}; }
};
// And another specialization is needed for void return.
template< typename ConcreteType >
struct Executor< void, ConcreteType >
{
enum : unsigned { SetUsed = 0 };
// No functions matched, so do nothing.
void operator () (const void *) { }
};
// This struct groups some helpers needed to define the recursive cases of
// Executor.
struct Dispatcher { struct Dispatcher {
// This implements the specialization of Executor //! First, recursive case of metafunction, defers generation of operator ()
// for the first recursive case.
template< typename R, typename ConcreteType, template< typename R, typename ConcreteType,
typename Function, typename ...Functions > typename Function, typename ...Functions >
struct inapplicable struct inapplicable
{ {
//! The template specialization to recur with
using Tail = Executor< R, ConcreteType, Functions... >; using Tail = Executor< R, ConcreteType, Functions... >;
//! Constant used in a compile-time check
enum : unsigned { SetUsed = Tail::SetUsed << 1 }; enum : unsigned { SetUsed = Tail::SetUsed << 1 };
// Ignore the first, inapplicable function and try others. //! Ignore the first, inapplicable function and try others.
R operator () R operator ()
(const Track *pTrack, (const Track *pTrack,
const Function &, const Functions &...functions) const Function &, const Functions &...functions)
{ return Tail{}( pTrack, functions... ); } { return Tail{}( pTrack, functions... ); }
}; };
// This implements the specialization of Executor //! Second, nonrecursive case of metafunction, generates operator () that calls function without fallthrough
// for the second recursive case.
template< typename R, typename BaseClass, typename ConcreteType, template< typename R, typename BaseClass, typename ConcreteType,
typename Function, typename ...Functions > typename Function, typename ...Functions >
struct applicable1 struct applicable1
{ {
//! Constant used in a compile-time check
enum : unsigned { SetUsed = 1u }; enum : unsigned { SetUsed = 1u };
// Ignore the remaining functions and call the first only. //! Ignore the remaining functions and call the first only.
R operator () R operator ()
(const Track *pTrack, (const Track *pTrack,
const Function &function, const Functions &...) const Function &function, const Functions &...)
{ return function( (BaseClass *)pTrack ); } { return function( (BaseClass *)pTrack ); }
}; };
// This implements the specialization of Executor //! Third, recursive case of metafunction, generates operator () that calls function with fallthrough
// for the third recursive case.
template< typename R, typename BaseClass, typename ConcreteType, template< typename R, typename BaseClass, typename ConcreteType,
typename Function, typename ...Functions > typename Function, typename ...Functions >
struct applicable2 struct applicable2
{ {
//! The template specialization to recur with
using Tail = Executor< R, ConcreteType, Functions... >; using Tail = Executor< R, ConcreteType, Functions... >;
//! Constant used in a compile-time check
enum : unsigned { SetUsed = (Tail::SetUsed << 1) | 1u }; enum : unsigned { SetUsed = (Tail::SetUsed << 1) | 1u };
// Call the first function, which may request dispatch to the further //! Call the first function, which may request dispatch to the further functions by invoking a continuation.
// functions by invoking a continuation.
R operator () R operator ()
(const Track *pTrack, const Function &function, (const Track *pTrack, const Function &function,
const Functions &...functions) const Functions &...functions)
@ -481,35 +479,40 @@ private:
} }
}; };
// This variadic template chooses among the implementations above. //! Variadic template implements metafunction with specializations, to choose among implementations of operator ()
template< typename ... > struct Switch; template< typename ... > struct Switch {};
// Ground the recursion. //! Base case, no more base classes of ConcreteType
/*! Computes a type as the return type of undefined member test() */
template< typename R, typename ConcreteType > template< typename R, typename ConcreteType >
struct Switch< R, ConcreteType > struct Switch< R, ConcreteType >
{ {
// No BaseClass of ConcreteType is acceptable to Function. //! No BaseClass of ConcreteType is acceptable to Function.
template< typename Function, typename ...Functions > template< typename Function, typename ...Functions >
static auto test() static auto test()
-> inapplicable< R, ConcreteType, Function, Functions... >; -> inapplicable< R, ConcreteType, Function, Functions... >;
}; };
// Recursive case. //! Recursive case, tries to match function with one base class of ConcreteType
/*! Computes a type as the return type of undefined member test() */
template< typename R, typename ConcreteType, template< typename R, typename ConcreteType,
typename BaseClass, typename ...BaseClasses > typename BaseClass, typename ...BaseClasses >
struct Switch< R, ConcreteType, BaseClass, BaseClasses... > struct Switch< R, ConcreteType, BaseClass, BaseClasses... >
{ {
//! Recur to this type to try the next base class
using Retry = Switch< R, ConcreteType, BaseClasses... >; using Retry = Switch< R, ConcreteType, BaseClasses... >;
// If ConcreteType is not compatible with BaseClass, or if //! Catch-all overload of undefined function used in decltype only
// Function does not accept BaseClass, try other BaseClasses. /*! If ConcreteType is not compatible with BaseClass, or if
Function does not accept BaseClass*, try other BaseClasses. */
template< typename Function, typename ...Functions > template< typename Function, typename ...Functions >
static auto test( const void * ) static auto test( const void * )
-> decltype( Retry::template test< Function, Functions... >() ); -> decltype( Retry::template test< Function, Functions... >() );
// If BaseClass is a base of ConcreteType and Function can take it, //! overload when upcast of ConcreteType* works, with sfinae'd return type
// then overload resolution chooses this. /*! If BaseClass is a base of ConcreteType and Function can take a pointer to it,
// If not, then the sfinae rule makes this overload unavailable. then overload resolution chooses this.
If not, then the sfinae rule makes this overload unavailable. */
template< typename Function, typename ...Functions > template< typename Function, typename ...Functions >
static auto test( std::true_type * ) static auto test( std::true_type * )
-> decltype( -> decltype(
@ -519,10 +522,11 @@ private:
Function, Functions... >{} Function, Functions... >{}
); );
// If BaseClass is a base of ConcreteType and Function can take it, //! overload when upcast of ConcreteType* works, with sfinae'd return type
// with a second argument for a continuation, /*! If BaseClass is a base of ConcreteType and Function can take a pointer to it,
// then overload resolution chooses this. with a second argument for a continuation,
// If not, then the sfinae rule makes this overload unavailable. then overload resolution chooses this.
If not, then the sfinae rule makes this overload unavailable. */
template< typename Function, typename ...Functions > template< typename Function, typename ...Functions >
static auto test( std::true_type * ) static auto test( std::true_type * )
-> decltype( -> decltype(
@ -533,8 +537,10 @@ private:
Function, Functions... >{} Function, Functions... >{}
); );
//! Whether upcast of ConcreteType* to first BaseClass* works
static constexpr bool Compatible = CompatibleTrackKinds( static constexpr bool Compatible = CompatibleTrackKinds(
track_kind<BaseClass>(), track_kind<ConcreteType>() ); track_kind<BaseClass>(), track_kind<ConcreteType>() );
//! undefined function used in decltype only to compute a type, using other overloads
template< typename Function, typename ...Functions > template< typename Function, typename ...Functions >
static auto test() static auto test()
-> decltype( -> decltype(
@ -543,7 +549,28 @@ private:
}; };
}; };
// This specialization is the recursive case for non-const tracks. //! Base case of metafunction implementing Track::TypeSwitch generates operator () with nonvoid return
template< typename R, typename ConcreteType >
struct Executor< R, ConcreteType >
{
//! Constant used in a compile-time check
enum : unsigned { SetUsed = 0 };
//! No functions matched, so do nothing
R operator () (const void *) { return R{}; }
};
//! Base case of metafunction implementing Track::TypeSwitch generates operator () with void return
template< typename ConcreteType >
struct Executor< void, ConcreteType >
{
//! Constant used in a compile-time check
enum : unsigned { SetUsed = 0 };
//! No functions matched, so do nothing
void operator () (const void *) { }
};
//! Implements Track::TypeSwitch, its operator() invokes the first function that can accept ConcreteType*
/*! Mutually recursive (in compile time) with template Track::Dispatcher. */
template< typename R, typename ConcreteType, template< typename R, typename ConcreteType,
typename Function, typename ...Functions > typename Function, typename ...Functions >
struct Executor< R, ConcreteType, Function, Functions... > struct Executor< R, ConcreteType, Function, Functions... >
@ -555,7 +582,8 @@ private:
::template test<Function, Functions... >()) ::template test<Function, Functions... >())
{}; {};
// This specialization is the recursive case for const tracks. //! Implements const overload of Track::TypeSwitch, its operator() invokes the first function that can accept ConcreteType*
/*! Mutually recursive (in compile time) with template Track::Dispatcher. */
template< typename R, typename ConcreteType, template< typename R, typename ConcreteType,
typename Function, typename ...Functions > typename Function, typename ...Functions >
struct Executor< R, const ConcreteType, Function, Functions... > struct Executor< R, const ConcreteType, Function, Functions... >
@ -569,17 +597,30 @@ private:
public: public:
// A variadic function taking any number of function objects, each taking //! Use this function rather than testing track type explicitly and making down-casts.
// a pointer to Track or a subclass, maybe const-qualified, and maybe a /*!
// second argument which is a fall-through continuation. A variadic function taking any number of function objects, each taking
// Each of the function objects (and supplied continuations) returns R. a pointer to Track or a subclass, maybe const-qualified, and maybe a
// Call the first in the sequence that accepts the actual type of the track. second argument which is a fall-through continuation.
// If no function accepts the track, do nothing and return R{}
// if R is not void. Each of the function objects (and supplied continuations) returns R (or a type convertible to R).
// If one of the functions invokes the call-through, then the next following Calls the first in the sequence that accepts the actual type of the track.
// applicable function is called.
template< typename R = void, typename ...Functions > If no function accepts the track, do nothing and return R{}
R TypeSwitch(const Functions &...functions) if R is not void.
If one of the functions invokes the call-through, then the next following
applicable function is called.
@tparam R Return type of this function and each function argument
@tparam Functions callable types deduced from arguments
@param functions typically lambdas, taking a pointer to a track subclass, and optionally a fall-through call-back
*/
template<
typename R = void,
typename ...Functions
>
R TypeSwitch( const Functions &...functions )
{ {
using WaveExecutor = using WaveExecutor =
Executor< R, WaveTrack, Functions... >; Executor< R, WaveTrack, Functions... >;
@ -618,9 +659,13 @@ public:
} }
} }
// This is the overload of TypeSwitch (see above) for const tracks, taking /*! @copydoc Track::TypeSwitch */
// callable arguments that only accept arguments that are pointers to const /*! This is the overload for const tracks, only taking
template< typename R = void, typename ...Functions > callable arguments that accept first arguments that are pointers to const. */
template<
typename R = void,
typename ...Functions
>
R TypeSwitch(const Functions &...functions) const R TypeSwitch(const Functions &...functions) const
{ {
using WaveExecutor = using WaveExecutor =
@ -702,6 +747,7 @@ protected:
std::shared_ptr<CommonTrackCell> mpControls; std::shared_ptr<CommonTrackCell> mpControls;
}; };
//! Track subclass holding data representing sound (as notes, or samples, or ...)
class AUDACITY_DLL_API AudioTrack /* not final */ : public Track class AUDACITY_DLL_API AudioTrack /* not final */ : public Track
{ {
public: public:
@ -717,6 +763,7 @@ public:
{ return false; } { return false; }
}; };
//! AudioTrack subclass that can also be audibly replayed by the program
class AUDACITY_DLL_API PlayableTrack /* not final */ : public AudioTrack class AUDACITY_DLL_API PlayableTrack /* not final */ : public AudioTrack
{ {
public: public:
@ -745,10 +792,14 @@ protected:
bool mSolo { false }; bool mSolo { false };
}; };
// Functions to encapsulate the checked down-casting of track pointers, //! Encapsulate the checked down-casting of track pointers
// eliminating possibility of error -- and not quietly casting away const /*! Eliminates possibility of error -- and not quietly casting away const
// typical usage:
// if (auto wt = track_cast<WaveTrack*>(track)) { ... } Typical usage:
```
if (auto wt = track_cast<const WaveTrack*>(track)) { ... }
```
*/
template<typename T> template<typename T>
inline typename std::enable_if< std::is_pointer<T>::value, T >::type inline typename std::enable_if< std::is_pointer<T>::value, T >::type
track_cast(Track *track) track_cast(Track *track)
@ -761,7 +812,8 @@ template<typename T>
return nullptr; return nullptr;
} }
// Overload for const pointers can cast only to other const pointer types /*! @copydoc track_cast(Track*)
This overload for const pointers can cast only to other const pointer types. */
template<typename T> template<typename T>
inline typename std::enable_if< inline typename std::enable_if<
std::is_pointer<T>::value && std::is_pointer<T>::value &&
@ -780,15 +832,22 @@ template<typename T>
template < typename TrackType > struct TrackIterRange; template < typename TrackType > struct TrackIterRange;
// new track iterators can eliminate the need to cast the result //! Iterator over only members of a TrackList of the specified subtype, optionally filtered by a predicate; past-end value dereferenceable, to nullptr
/*! Does not suffer invalidation when an underlying std::list iterator is deleted, provided that is not
equal to its current position or to the beginning or end iterator.
The filtering predicate is tested only when the iterator is constructed or advanced.
@tparam TrackType Track or a subclass, maybe const-qualified
*/
template < template <
typename TrackType // Track or a subclass, maybe const-qualified typename TrackType
> class TrackIter > class TrackIter
: public ValueIterator< TrackType *, std::bidirectional_iterator_tag > : public ValueIterator< TrackType *, std::bidirectional_iterator_tag >
{ {
public: public:
// Type of predicate taking pointer to const TrackType //! Type of predicate taking pointer to const TrackType
// TODO C++14: simplify away ::type /*! @todo C++14: simplify away ::type */
using FunctionType = std::function< bool( using FunctionType = std::function< bool(
typename std::add_pointer< typename std::add_pointer<
typename std::add_const< typename std::add_const<
@ -799,28 +858,32 @@ public:
>::type >::type
) >; ) >;
template<typename Predicate = FunctionType> //! Constructor, usually not called directly except by methods of TrackList
TrackIter( TrackNodePointer begin, TrackNodePointer iter, TrackIter(
TrackNodePointer end, const Predicate &pred = {} ) TrackNodePointer begin, //!< Remember lower bound
: mBegin( begin ), mIter( iter ), mEnd( end ), mPred( pred ) TrackNodePointer iter, //!< The actual pointer
TrackNodePointer end, //!< Remember upper bound
FunctionType pred = {} //!< %Optional filter
)
: mBegin( begin ), mIter( iter ), mEnd( end )
, mPred( std::move(pred) )
{ {
// Establish the class invariant // Establish the class invariant
if (this->mIter != this->mEnd && !this->valid()) if (this->mIter != this->mEnd && !this->valid())
this->operator ++ (); this->operator ++ ();
} }
// Return an iterator that replaces the predicate, advancing to the first //! Return an iterator that replaces the predicate
// position at or after the old position that satisfies the new predicate, /*! Advance to the first position at or after the old position that satisfies the new predicate,
// or to the end. or to the end */
template < typename Predicate2 > TrackIter Filter( FunctionType pred2 ) const
TrackIter Filter( const Predicate2 &pred2 ) const
{ {
return { this->mBegin, this->mIter, this->mEnd, pred2 }; return { this->mBegin, this->mIter, this->mEnd, std::move(pred2) };
} }
// Return an iterator that refines the subclass (and not removing const), //! Return an iterator for a subclass of TrackType (and not removing const) with same predicate
// advancing to the first position at or after the old position that /*! Advance to the first position at or after the old position that
// satisfies the type constraint, or to the end satisfies the type constraint, or to the end */
template < typename TrackType2 > template < typename TrackType2 >
auto Filter() const auto Filter() const
-> typename std::enable_if< -> typename std::enable_if<
@ -836,8 +899,8 @@ public:
const FunctionType &GetPredicate() const const FunctionType &GetPredicate() const
{ return this->mPred; } { return this->mPred; }
// Unlike with STL iterators, this class gives well defined behavior when //! Safe to call even when at the end
// you increment an end iterator: you get the same. /*! In that case *this remains unchanged. */
TrackIter &operator ++ () TrackIter &operator ++ ()
{ {
// Maintain the class invariant // Maintain the class invariant
@ -847,6 +910,7 @@ public:
return *this; return *this;
} }
//! @copydoc operator++
TrackIter operator ++ (int) TrackIter operator ++ (int)
{ {
TrackIter result { *this }; TrackIter result { *this };
@ -854,9 +918,8 @@ public:
return result; return result;
} }
// Unlike with STL iterators, this class gives well defined behavior when //! Safe to call even when at the beginning
// you decrement past the beginning of a range: you wrap and get an end /*! In that case *this wraps to the end. */
// iterator.
TrackIter &operator -- () TrackIter &operator -- ()
{ {
// Maintain the class invariant // Maintain the class invariant
@ -870,6 +933,7 @@ public:
return *this; return *this;
} }
//! @copydoc operator--
TrackIter operator -- (int) TrackIter operator -- (int)
{ {
TrackIter result { *this }; TrackIter result { *this };
@ -877,8 +941,8 @@ public:
return result; return result;
} }
// Unlike with STL iterators, this class gives well defined behavior when //! Safe to call even when at the end
// you dereference an end iterator: you get a null pointer. /*! In that case you get nullptr. */
TrackType *operator * () const TrackType *operator * () const
{ {
if (this->mIter == this->mEnd) if (this->mIter == this->mEnd)
@ -890,16 +954,17 @@ public:
return static_cast< TrackType * >( &**this->mIter.first ); return static_cast< TrackType * >( &**this->mIter.first );
} }
// This might be called operator + , //! This might be called operator + , but it's not constant-time as with a random access iterator
// but that might wrongly suggest constant time when the iterator is not TrackIter advance(
// random access. long amount //!< may be negative
TrackIter advance( long amount ) const ) const
{ {
auto copy = *this; auto copy = *this;
std::advance( copy, amount ); std::advance( copy, amount );
return copy; return copy;
} }
//! Compares only current positions, assuming same beginnings and ends
friend inline bool operator == (TrackIter a, TrackIter b) friend inline bool operator == (TrackIter a, TrackIter b)
{ {
// Assume the predicate is not stateful. Just compare the iterators. // Assume the predicate is not stateful. Just compare the iterators.
@ -910,12 +975,18 @@ public:
; ;
} }
//! @copydoc operator==
friend inline bool operator != (TrackIter a, TrackIter b) friend inline bool operator != (TrackIter a, TrackIter b)
{ {
return !(a == b); return !(a == b);
} }
private: private:
/*! @pre underlying iterators are still valid, and mPred, if not null, is well behaved */
/*! @invariant mIter == mEnd, or else, mIter != mEnd,
and **mIter is of the appropriate subclass, and mPred is null or mPred(&**mIter) is true. */
//! Test satisfaction of the invariant, while initializing, incrementing, or decrementing
bool valid() const bool valid() const
{ {
// assume mIter != mEnd // assume mIter != mEnd
@ -925,16 +996,17 @@ private:
return !this->mPred || this->mPred( pTrack ); return !this->mPred || this->mPred( pTrack );
} }
// This friendship is needed in TrackIterRange::StartingWith and //! This friendship is needed in TrackIterRange::StartingWith and TrackIterRange::EndingAfter()
// TrackIterRange::EndingAfter()
friend TrackIterRange< TrackType >; friend TrackIterRange< TrackType >;
// The class invariant is that mIter == mEnd, or else, mIter != mEnd and TrackNodePointer
// **mIter is of the appropriate subclass and mPred(&**mIter) is true. mBegin, //!< Allows end of reverse iteration to be detected without comparison to other TrackIter
TrackNodePointer mBegin, mIter, mEnd; mIter, //!< Current position
FunctionType mPred; mEnd; //!< Allows end of iteration to be detected without comparison to other TrackIter
FunctionType mPred; //!< %Optional filter
}; };
//! Range between two @ref TrackIter "TrackIters", usable in range-for statements, and with Visit member functions
template < template <
typename TrackType // Track or a subclass, maybe const-qualified typename TrackType // Track or a subclass, maybe const-qualified
> struct TrackIterRange > struct TrackIterRange
@ -1036,7 +1108,7 @@ template <
[=](const Track *pTrack){ return pExcluded == pTrack; } ); [=](const Track *pTrack){ return pExcluded == pTrack; } );
} }
// See Track::TypeSwitch //! See Track::TypeSwitch
template< typename ...Functions > template< typename ...Functions >
void Visit(const Functions &...functions) void Visit(const Functions &...functions)
{ {
@ -1044,8 +1116,8 @@ template <
track->TypeSwitch(functions...); track->TypeSwitch(functions...);
} }
// See Track::TypeSwitch //! See Track::TypeSwitch
// Visit until flag is false, or no more tracks /*! Visit until flag is false, or no more tracks */
template< typename Flag, typename ...Functions > template< typename Flag, typename ...Functions >
void VisitWhile(Flag &flag, const Functions &...functions) void VisitWhile(Flag &flag, const Functions &...functions)
{ {
@ -1058,6 +1130,7 @@ template <
}; };
//! Notification of changes in individual tracks of TrackList, or of TrackList's composition
struct TrackListEvent : public wxCommandEvent struct TrackListEvent : public wxCommandEvent
{ {
explicit explicit
@ -1079,40 +1152,38 @@ struct TrackListEvent : public wxCommandEvent
int mCode; int mCode;
}; };
// Posted when the set of selected tracks changes. //! Posted when the set of selected tracks changes.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACKLIST_SELECTION_CHANGE, TrackListEvent); EVT_TRACKLIST_SELECTION_CHANGE, TrackListEvent);
// Posted when certain fields of a track change. //! Posted when certain fields of a track change.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent); EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent);
// Posted when a track needs to be scrolled into view. //! Posted when a track needs to be scrolled into view.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACKLIST_TRACK_REQUEST_VISIBLE, TrackListEvent); EVT_TRACKLIST_TRACK_REQUEST_VISIBLE, TrackListEvent);
// Posted when tracks are reordered but otherwise unchanged. //! Posted when tracks are reordered but otherwise unchanged.
// mpTrack points to the moved track that is earliest in the New ordering. /*! mpTrack points to the moved track that is earliest in the New ordering. */
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACKLIST_PERMUTED, TrackListEvent); EVT_TRACKLIST_PERMUTED, TrackListEvent);
// Posted when some track changed its height. //! Posted when some track changed its height.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACKLIST_RESIZING, TrackListEvent); EVT_TRACKLIST_RESIZING, TrackListEvent);
// Posted when a track has been added to a tracklist. //! Posted when a track has been added to a tracklist. Also posted when one track replaces another
// Also posted when one track replaces another
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACKLIST_ADDITION, TrackListEvent); EVT_TRACKLIST_ADDITION, TrackListEvent);
// Posted when a track has been deleted from a tracklist. //! Posted when a track has been deleted from a tracklist. Also posted when one track replaces another
// Also posted when one track replaces another. /*! mpTrack points to the first track after the deletion, if there is one. */
// mpTrack points to the first track after the deletion, if there is one.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_TRACKLIST_DELETION, TrackListEvent); EVT_TRACKLIST_DELETION, TrackListEvent);
/** \brief TrackList is a flat linked list of tracks supporting Add, Remove, /*! @brief A flat linked list of tracks supporting Add, Remove,
* Clear, and Contains, plus serialization of the list of tracks. * Clear, and Contains, serialization of the list of tracks, event notifications
*/ */
class TrackList final class TrackList final
: public wxEvtHandler : public wxEvtHandler
@ -1170,7 +1241,7 @@ class TrackList final
const_iterator cbegin() const { return begin(); } const_iterator cbegin() const { return begin(); }
const_iterator cend() const { return end(); } const_iterator cend() const { return end(); }
// Turn a pointer into an iterator (constant time). //! Turn a pointer into a TrackIter (constant time); get end iterator if this does not own the track
template < typename TrackType = Track > template < typename TrackType = Track >
auto Find(Track *pTrack) auto Find(Track *pTrack)
-> TrackIter< TrackType > -> TrackIter< TrackType >
@ -1181,7 +1252,8 @@ class TrackList final
return MakeTrackIterator<TrackType>( pTrack->GetNode() ); return MakeTrackIterator<TrackType>( pTrack->GetNode() );
} }
// Turn a pointer into an iterator (constant time). //! @copydoc Find
/*! const overload will only produce iterators over const TrackType */
template < typename TrackType = const Track > template < typename TrackType = const Track >
auto Find(const Track *pTrack) const auto Find(const Track *pTrack) const
-> typename std::enable_if< std::is_const<TrackType>::value, -> typename std::enable_if< std::is_const<TrackType>::value,
@ -1330,7 +1402,7 @@ public:
friend class Track; friend class Track;
/// For use in sorting: assume each iterator points into this list, no duplications //! For use in sorting: assume each iterator points into this list, no duplications
void Permute(const std::vector<TrackNodePointer> &permutation); void Permute(const std::vector<TrackNodePointer> &permutation);
Track *FindById( TrackId id ); Track *FindById( TrackId id );
@ -1359,8 +1431,7 @@ public:
ListOfTracks::value_type Replace( ListOfTracks::value_type Replace(
Track * t, const ListOfTracks::value_type &with); Track * t, const ListOfTracks::value_type &with);
/// Remove this Track or all children of this TrackList. //! Remove the Track and return an iterator to what followed it.
/// Return an iterator to what followed the removed track.
TrackNodePointer Remove(Track *t); TrackNodePointer Remove(Track *t);
/// Make the list empty /// Make the list empty
@ -1373,7 +1444,7 @@ public:
bool MoveDown(Track * t); bool MoveDown(Track * t);
bool Move(Track * t, bool up) { return up ? MoveUp(t) : MoveDown(t); } bool Move(Track * t, bool up) { return up ? MoveUp(t) : MoveDown(t); }
/// Mainly a test function. Uses a linear search, so could be slow. //! Mainly a test function. Uses a linear search, so could be slow.
bool Contains(const Track * t) const; bool Contains(const Track * t) const;
// Return non-null only if the weak pointer is not, and the track is // Return non-null only if the weak pointer is not, and the track is
@ -1463,7 +1534,7 @@ private:
{ return { const_cast<TrackList*>(this)->ListOfTracks::begin(), { return { const_cast<TrackList*>(this)->ListOfTracks::begin(),
const_cast<TrackList*>(this)}; } const_cast<TrackList*>(this)}; }
// Move an iterator to the next node, if any; else stay at end //! Move an iterator to the next node, if any; else stay at end
TrackNodePointer getNext(TrackNodePointer p) const TrackNodePointer getNext(TrackNodePointer p) const
{ {
if ( isNull(p) ) if ( isNull(p) )
@ -1473,7 +1544,7 @@ private:
return q; return q;
} }
// Move an iterator to the previous node, if any; else wrap to end //! Move an iterator to the previous node, if any; else wrap to end
TrackNodePointer getPrev(TrackNodePointer p) const TrackNodePointer getPrev(TrackNodePointer p) const
{ {
if (p == getBegin()) if (p == getBegin())
@ -1550,9 +1621,10 @@ public:
private: private:
AudacityProject *mOwner; AudacityProject *mOwner;
// Need to put pending tracks into a list so that GetLink() works //! Shadow tracks holding append-recording in progress; need to put them into a list so that GetLink() works
/*! Beware, they are in a disjoint iteration sequence from ordinary tracks */
ListOfTracks mPendingUpdates; ListOfTracks mPendingUpdates;
// This is in correspondence with mPendingUpdates //! This is in correspondence with mPendingUpdates
std::vector< Updater > mUpdaters; std::vector< Updater > mUpdaters;
}; };