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