1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
/**********************************************************************
Audacity: A Digital Audio Editor
XMLMethodRegistry.h
Paul Licameli
**********************************************************************/
#ifndef __AUDACITY_XML_METHOD_REGISTRY__
#define __AUDACITY_XML_METHOD_REGISTRY__
#include <forward_list>
#include <string>
#include <string_view>
#include <functional>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
class XMLTagHandler;
class XMLWriter;
class XMLAttributeValueView;
//! Implementation helper for ProjectFileIORegistry.
/*! It makes most of the work non-inline and is used by derived classes that
supply thin inline type-erasing wrappers. */
class XML_API XMLMethodRegistryBase {
public:
//! A helper type alias for a function taking a structure and a string value
template< typename Substructure >
using Mutator = std::function< void(Substructure&, const XMLAttributeValueView &) >;
//! A helper type alias for a list of mutators, associated with tag strings
template< typename Substructure >
using Mutators = std::vector< std::pair< std::string, Mutator<Substructure> > >;
XMLMethodRegistryBase();
~XMLMethodRegistryBase();
protected:
using TypeErasedObjectAccessor = std::function< XMLTagHandler *( void* ) >;
using TagTable =
std::unordered_map< std::string_view, TypeErasedObjectAccessor >;
TagTable mTagTable;
std::forward_list<std::string> mTags;
void Register(std::string tag, TypeErasedObjectAccessor accessor);
XMLTagHandler *CallObjectAccessor( const std::string_view &tag, void *p );
using TypeErasedAccessor = std::function< void*( void* ) >;
using TypeErasedAccessors = std::vector< TypeErasedAccessor >;
TypeErasedAccessors mAccessors;
void PushAccessor( TypeErasedAccessor accessor );
using TypeErasedMutator = std::function< void( void*, const XMLAttributeValueView& ) >;
//! From attribute name, to index in accessor table with a mutator
using MutatorTable = std::unordered_map<std::string_view,
std::pair<size_t, TypeErasedMutator>>;
MutatorTable mMutatorTable;
std::forward_list<std::string> mMutatorTags;
void Register(std::string tag, TypeErasedMutator mutator);
bool CallAttributeHandler(const std::string_view& tag,
void *p, const XMLAttributeValueView &value );
using TypeErasedWriter = std::function< void(const void *, XMLWriter &) >;
using WriterTable = std::vector< TypeErasedWriter >;
WriterTable mAttributeWriterTable;
void RegisterAttributeWriter( TypeErasedWriter writer );
void CallAttributeWriters( const void *p, XMLWriter &writer );
WriterTable mObjectWriterTable;
void RegisterObjectWriter( TypeErasedWriter writer );
void CallObjectWriters( const void *p, XMLWriter &writer );
};
/*! A class template of inline type-erasing wrapper functions, but one function
with linkage is also needed. See the macros that help generate that function.
*/
template< typename Host >
class XMLMethodRegistry : public XMLMethodRegistryBase {
public:
// Typically statically constructed
struct ObjectReaderEntry {
template <
/*!
This "accessor" may or may not act as a "factory" that builds a new object and
may return nullptr. Caller of the accessor is not responsible for the object
lifetime, which is assumed to outlast the project loading procedure.
*/
typename ObjectAccessor /*!< Often a lambda.
A function from Host& to XMLTagHandler*, maybe returning null */
>
ObjectReaderEntry( const std::string &tag, ObjectAccessor fn )
{
// Remember the function, type-erased
Get().Register( tag, [ fn = std::move(fn) ] (void *p) {
// CallObjectAccessor will guarantee p is not null
return fn( *static_cast<Host *>(p) );
} );
}
};
XMLTagHandler *CallObjectAccessor(const std::string_view& tag, Host& host)
{
return XMLMethodRegistryBase::CallObjectAccessor( tag, &host );
}
/*! Typically statically constructed */
/*!
Registers procedures to update the host for certain attributes, in two
steps: first the `accessor` which fetches some substructure owned by the
host, which is the common step; then, the `mutators` that rewrite the
substructure, each of them associated with one attribute string, and
taking a reference to the substructure and a value string.
*/
struct AttributeReaderEntries {
template<
typename Accessor, /*!< Often a lambda.
Takes const Host& and returns Substructure& */
typename Substructure //<! Type deduction of the return of Accessor
= std::remove_reference_t< decltype(
std::declval<Accessor>()( std::declval<Host &>() )
) >
>
AttributeReaderEntries( Accessor fn, Mutators<Substructure> pairs )
{
// Remember the functions, type-erased
auto ®istry = Get();
registry.PushAccessor(
[ fn = std::move(fn) ] ( void *p ) {
// CallAttributeHandler will guarantee p is not null
return &fn( *static_cast<Host *>(p) ); }
);
for (auto &pair : pairs)
registry.Register( pair.first,
[ fn = move(pair.second) ]( auto p, auto value ){
fn( *static_cast<Substructure*>(p), value ); }
);
}
};
// @return whether any function was found and called for the tag
bool CallAttributeHandler(
const std::string_view &tag, Host &host, const XMLAttributeValueView& value )
{
return XMLMethodRegistryBase::CallAttributeHandler( tag, &host, value );
}
//! Typically statically constructed
struct AttributeWriterEntry {
template <
/*!
The Writer may write any number of XML attributes, but must not write tags.
So there should be some reader entries corresponding to each writer, and that
may be many-to-one.
The readers must not make assumptions about the sequence in which they will
be called.
*/
typename Writer /*!< Often a lambda.
Takes const Host& and XMLWriter& and returns void */
>
explicit AttributeWriterEntry( Writer fn )
{
// Remember the function, type-erased
Get().RegisterAttributeWriter(
[ fn = std::move(fn) ] ( const void *p, XMLWriter &writer ) {
// CallObjectAccessor will guarantee p is not null
return fn( *static_cast<const Host *>(p), writer );
} );
}
};
//! Typically statically constructed
struct ObjectWriterEntry {
template <
/*!
The Writer may write any number of XML tags, but no attributes.
So there should be some reader entries corresponding to each writer, and that
may be many-to-one.
The readers must not make assumptions about the sequence in which they will
be called.
*/
typename Writer /*!< Often a lambda.
Takes const Host& and XMLWriter& and returns void */
>
explicit ObjectWriterEntry( Writer fn )
{
// Remember the function, type-erased
Get().RegisterObjectWriter(
[ fn = std::move(fn) ] ( const void *p, XMLWriter &writer ) {
// CallObjectAccessor will guarantee p is not null
return fn( *static_cast<const Host *>(p), writer );
} );
}
};
void CallWriters( const Host &host, XMLWriter &writer )
{
XMLMethodRegistryBase::CallAttributeWriters( &host, writer );
XMLMethodRegistryBase::CallObjectWriters( &host, writer );
}
//! Get the unique instance
static XMLMethodRegistry &Get();
};
/*! Typically follows the `using` declaration of an XMLMethodRegistry
specialization; DECLSPEC is for linkage visibility */
#define DECLARE_XML_METHOD_REGISTRY(DECLSPEC, Name) \
template<> auto DECLSPEC Name::Get() -> Name &;
/*! Typically in the companion .cpp file */
#define DEFINE_XML_METHOD_REGISTRY(Name) \
template<> auto Name::Get() -> Name & \
{ \
static Name registry; \
return registry; \
}
#endif
|