Define class Identifier and template TaggedIdentifier...
... Identifier holds strings used for internal purposes and not shown to users; TaggedIdentifier generates subclasses of Identifier for different purposes, which won't implicitly (that is, inadvertently) interconvert as function arguments.
This commit is contained in:
parent
172c8abe03
commit
3eeb91f23a
|
@ -46,11 +46,9 @@
|
|||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <wx/debug.h> // for wxASSERT
|
||||
#include <wx/string.h> // type used in inline function
|
||||
#include <wx/string.h> // type used in inline function and member variable
|
||||
#include <wx/version.h> // for wxCHECK_VERSION
|
||||
|
||||
class wxString;
|
||||
|
||||
#if !wxCHECK_VERSION(3, 1, 0)
|
||||
// For using std::unordered_map on wxString
|
||||
namespace std
|
||||
|
@ -73,6 +71,199 @@ namespace std
|
|||
// until proper public headers are created for the stuff in here.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// An explicitly nonlocalized string, not meant for the user to see.
|
||||
// String manipulations are discouraged, other than splitting and joining on
|
||||
// separator characters.
|
||||
// Wherever GET is used to fetch the underlying wxString, there should be a
|
||||
// comment explaining the need for it.
|
||||
class Identifier
|
||||
{
|
||||
public:
|
||||
|
||||
Identifier() = default;
|
||||
|
||||
// Allow implicit conversion to this class, but not from
|
||||
Identifier(const wxString &str) : value{ str } {}
|
||||
|
||||
// Allow implicit conversion to this class, but not from
|
||||
Identifier(const wxChar *str) : value{ str } {}
|
||||
|
||||
// Allow implicit conversion to this class, but not from
|
||||
Identifier(const char *str) : value{ str } {}
|
||||
|
||||
// Copy construction and assignment
|
||||
Identifier( const Identifier & ) = default;
|
||||
Identifier &operator = ( const Identifier& ) = default;
|
||||
|
||||
// Move construction and assignment
|
||||
Identifier( wxString && str )
|
||||
{ value.swap( str ); }
|
||||
Identifier( Identifier && id )
|
||||
{ swap( id ); }
|
||||
Identifier &operator= ( Identifier&& id )
|
||||
{
|
||||
if ( this != &id )
|
||||
value.clear(), swap( id );
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Implements moves
|
||||
void swap( Identifier &id ) { value.swap( id.value ); }
|
||||
|
||||
// Convenience for building concatenated identifiers.
|
||||
// The list must have at least two members
|
||||
// (so you don't easily circumvent the restrictions on interconversions
|
||||
// intended in TaggedIdentifier below)
|
||||
explicit
|
||||
Identifier(std::initializer_list<Identifier> components, wxChar separator);
|
||||
|
||||
bool empty() const { return value.empty(); }
|
||||
size_t size() const { return value.size(); }
|
||||
size_t length() const { return value.length(); }
|
||||
|
||||
// Explicit conversion to wxString, meant to be ugly-looking and
|
||||
// demanding of a comment why it's correct
|
||||
const wxString &GET() const { return value; }
|
||||
|
||||
std::vector< Identifier > split( wxChar separator ) const;
|
||||
|
||||
private:
|
||||
wxString value;
|
||||
};
|
||||
|
||||
// Comparisons of Identifiers are case-sensitive
|
||||
inline bool operator == ( const Identifier &x, const Identifier &y )
|
||||
{ return x.GET() == y.GET(); }
|
||||
|
||||
inline bool operator != ( const Identifier &x, const Identifier &y )
|
||||
{ return !(x == y); }
|
||||
|
||||
inline bool operator < ( const Identifier &x, const Identifier &y )
|
||||
{ return x.GET() < y.GET(); }
|
||||
|
||||
inline bool operator > ( const Identifier &x, const Identifier &y )
|
||||
{ return y < x; }
|
||||
|
||||
inline bool operator <= ( const Identifier &x, const Identifier &y )
|
||||
{ return !(y < x); }
|
||||
|
||||
inline bool operator >= ( const Identifier &x, const Identifier &y )
|
||||
{ return !(x < y); }
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash< Identifier > {
|
||||
size_t operator () ( const Identifier &id ) const // noexcept
|
||||
{ return hash<wxString>{}( id.GET() ); }
|
||||
};
|
||||
}
|
||||
|
||||
// This lets you pass Identifier into wxFileConfig::Read
|
||||
inline bool wxFromString(const wxString& str, Identifier *id)
|
||||
{ if (id) { *id = str; return true; } else return false; }
|
||||
|
||||
// This lets you pass Identifier into wxFileConfig::Write
|
||||
inline wxString wxToString( const Identifier& str ) { return str.GET(); }
|
||||
|
||||
// Template parameter allows generation of different TaggedIdentifier classes
|
||||
// that don't interconvert implicitly
|
||||
// The second template parameter determines whether comparisons are case
|
||||
// sensitive; default is case sensitive
|
||||
template<typename Tag, bool CaseSensitive = true >
|
||||
class TaggedIdentifier : public Identifier
|
||||
{
|
||||
public:
|
||||
|
||||
using TagType = Tag;
|
||||
|
||||
using Identifier::Identifier;
|
||||
TaggedIdentifier() = default;
|
||||
|
||||
// Allowed for the same Tag class and case sensitivity
|
||||
TaggedIdentifier( const TaggedIdentifier& ) = default;
|
||||
TaggedIdentifier( TaggedIdentifier&& ) = default;
|
||||
TaggedIdentifier& operator= ( const TaggedIdentifier& ) = default;
|
||||
TaggedIdentifier& operator= ( TaggedIdentifier&& ) = default;
|
||||
|
||||
// Prohibited for other Tag classes or case sensitivity
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier( const TaggedIdentifier<Tag2, b>& ) = delete;
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier( TaggedIdentifier<Tag2, b>&& ) = delete;
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier& operator= ( const TaggedIdentifier<Tag2, b>& ) = delete;
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier& operator= ( TaggedIdentifier<Tag2, b>&& ) = delete;
|
||||
|
||||
// Allow implicit conversion to this class from un-tagged Identifier,
|
||||
// but not from; resolution will use other overloads above if argument
|
||||
// has a tag
|
||||
TaggedIdentifier(const Identifier &str) : Identifier{ str } {}
|
||||
|
||||
// Conversion to another kind of TaggedIdentifier
|
||||
template<typename String, typename = typename String::TagType>
|
||||
String CONVERT() const
|
||||
{ return String{ this->GET() }; }
|
||||
};
|
||||
|
||||
// Comparison of a TaggedIdentifier with an Identifier is allowed, resolving
|
||||
// to one of the operators on Identifiers defined above, but always case
|
||||
// sensitive.
|
||||
|
||||
// Comparison operators for two TaggedIdentifers, below, require the same tags
|
||||
// and case sensitivity.
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator == (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{
|
||||
static_assert( std::is_same< Tag1, Tag2 >::value && b1 == b2,
|
||||
"TaggedIdentifiers with different tags or sensitivity are not comparable" );
|
||||
// This test should be eliminated at compile time:
|
||||
if ( b1 )
|
||||
return x.GET(). Cmp ( y.GET() ) == 0;
|
||||
else
|
||||
return x.GET(). CmpNoCase ( y.GET() ) == 0;
|
||||
}
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator != (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return !(x == y); }
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator < (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{
|
||||
static_assert( std::is_same< Tag1, Tag2 >::value && b1 == b2,
|
||||
"TaggedIdentifiers with different tags or sensitivity are not comparable" );
|
||||
// This test should be eliminated at compile time:
|
||||
if ( b1 )
|
||||
return x.GET(). Cmp ( y.GET() ) < 0;
|
||||
else
|
||||
return x.GET(). CmpNoCase ( y.GET() ) < 0;
|
||||
}
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator > (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return y < x; }
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator <= (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return !(y < x); }
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator >= (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return !(x < y); }
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename Tag, bool b > struct hash< TaggedIdentifier<Tag, b> >
|
||||
: hash< Identifier > {};
|
||||
}
|
||||
|
||||
/**************************************************************************//**
|
||||
|
||||
\brief type alias for identifying a Plugin supplied by a module, each module
|
||||
|
|
|
@ -310,3 +310,25 @@ wxArrayStringEx LocalizedStrings(
|
|||
std::mem_fn( &EnumValueSymbol::Translation )
|
||||
);
|
||||
}
|
||||
|
||||
// Find a better place for this?
|
||||
#include "audacity/Types.h"
|
||||
Identifier::Identifier(
|
||||
std::initializer_list<Identifier> components, wxChar separator )
|
||||
{
|
||||
if( components.size() < 2 )
|
||||
{
|
||||
wxASSERT( false );
|
||||
return;
|
||||
}
|
||||
auto iter = components.begin(), end = components.end();
|
||||
value = (*iter++).value;
|
||||
while (iter != end)
|
||||
value += separator + (*iter++).value;
|
||||
}
|
||||
|
||||
std::vector< Identifier > Identifier::split( wxChar separator ) const
|
||||
{
|
||||
auto strings = ::wxSplit( value, separator );
|
||||
return { strings.begin(), strings.end() };
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
class wxArrayString;
|
||||
class wxArrayStringEx;
|
||||
class wxString;
|
||||
|
||||
extern AUDACITY_DLL_API const wxString& GetCustomTranslation(const wxString& str1 );
|
||||
extern AUDACITY_DLL_API const wxString& GetCustomSubstitution(const wxString& str1 );
|
||||
|
|
|
@ -30,6 +30,12 @@ class AUDACITY_DLL_API XMLWriter /* not final */ {
|
|||
virtual void StartTag(const wxString &name);
|
||||
virtual void EndTag(const wxString &name);
|
||||
|
||||
// nonvirtual pass-through
|
||||
void WriteAttr(const wxString &name, const Identifier &value)
|
||||
// using GET once here, permitting Identifiers in XML,
|
||||
// so no need for it at each WriteAttr call
|
||||
{ WriteAttr( name, value.GET() ); }
|
||||
|
||||
virtual void WriteAttr(const wxString &name, const wxString &value);
|
||||
virtual void WriteAttr(const wxString &name, const wxChar *value);
|
||||
|
||||
|
|
Loading…
Reference in New Issue