]> git.tdb.fi Git - libs/datafile.git/blob - source/collection.h
Support const types with Collection properly
[libs/datafile.git] / source / collection.h
1 /* $Id$
2
3 This file is part of libmspdatafile
4 Copyright © 2006  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #ifndef MSP_DATAFILE_COLLECTION_H_
9 #define MSP_DATAFILE_COLLECTION_H_
10
11 #include <msp/core/refptr.h>
12 #include "loader.h"
13
14 namespace Msp {
15 namespace DataFile {
16
17 /**
18 Helper struct to determine whether a Loader has a Collection typedef.
19 */
20 template<typename T>
21 struct NeedsCollection
22 {
23         struct Yes { char c[2]; };
24         struct No { char c; };
25
26         template<typename U>
27         static Yes f(typename U::Collection *);
28         template<typename U>
29         static No f(...);
30
31         enum { result=(sizeof(f<T>(0))==sizeof(Yes)) };
32 };
33
34 template<typename T>
35 struct RemoveConst
36 { typedef T Type; };
37
38 template<typename T>
39 struct RemoveConst<const T>
40 { typedef T Type; };
41
42 /**
43 A collection of objects that can be loaded from a datafile.  Each object is
44 identified by a name, which must be unique across the entire collection.
45 */
46 class Collection
47 {
48 public:
49         class Loader;
50
51 private:
52         /* XXX I don't really like sticking all this stuff in here, but there's some
53            complex inter-class relationships, especially between ItemKeyword and
54            Collection::Loader. */
55
56         struct ItemBase
57         {
58                 virtual ~ItemBase() { }
59         };
60
61         template<typename T>
62         struct Item: public ItemBase
63         {
64                 T *data;
65
66                 Item(T *d): data(d) { }
67                 ~Item() { delete data; }
68         };
69
70         /**
71         Used to store keywords for types that can be loaded.
72         */
73         struct ItemKeywordBase
74         {
75                 virtual void add_to_loader(Loader &) const { };
76         };
77
78         template<typename T, typename S, bool need_coll=NeedsCollection<typename T::Loader>::result>
79         struct ItemKeyword: public ItemKeywordBase
80         {
81                 std::string keyword;
82
83                 ItemKeyword(const std::string &kw): keyword(kw) { }
84
85                 void add_to_loader(Loader &ldr) const
86                 { ldr.add(keyword, &Loader::item<T, S>); }
87         };
88
89         template<typename T, typename S>
90         struct ItemKeyword<T, S, true>: public ItemKeywordBase
91         {
92                 std::string keyword;
93
94                 ItemKeyword(const std::string &kw): keyword(kw) { }
95
96                 virtual void add_to_loader(Loader &ldr) const
97                 { ldr.add(keyword, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
98         };
99
100         /**
101         Used to store types that can be created automatically.
102         */
103         struct ItemCreatorBase
104         {
105                 virtual ~ItemCreatorBase() { }
106
107                 template<typename S>
108                 S *create(Collection &coll, const std::string &name)
109                 {
110                         ItemCreatorBridge<S> *creator=dynamic_cast<ItemCreatorBridge<S> *>(this);
111                         if(creator)
112                                 return creator->create(coll, name);
113                         return 0;
114                 }
115         };
116
117         template<typename S>
118         struct ItemCreatorBridge: public ItemCreatorBase
119         {
120                 virtual S *create(Collection &, const std::string &) const =0;
121         };
122
123         template<typename T, typename S, typename C>
124         struct ItemCreator: public ItemCreatorBridge<S>
125         {
126                 typedef T *(C::*fCreate)(const std::string &);
127
128                 fCreate create_func;
129
130                 ItemCreator(fCreate cf): create_func(cf) { }
131                 virtual S *create(Collection &coll, const std::string &name) const
132                 { return (dynamic_cast<C &>(coll).*create_func)(name); }
133         };
134
135 public:
136         /**
137         Loads objects into a Collection.
138         */
139         class Loader: public DataFile::Loader
140         {
141         private:
142                 Collection &coll;
143
144         public:
145                 Loader(Collection &);
146                 Collection &get_object() const { return coll; }
147         private:
148                 template<typename T, typename S, typename C>
149                 void coll_item(const std::string &n)
150                 {
151                         RefPtr<T> it=new T;
152                         load_sub(*it, dynamic_cast<C &>(coll));
153                         coll.add<S>(n, it.get());
154                         it.release();
155                 }
156
157                 template<typename T, typename S>
158                 void item(const std::string &n)
159                 {
160                         RefPtr<T> it=new T;
161                         load_sub(*it);
162                         coll.add<S>(n, it.get());
163                         it.release();
164                 }
165
166                 template<typename, typename, bool> friend class ItemKeyword;
167         };
168
169 private:
170         typedef std::map<std::string, ItemBase *> ItemMap;
171         typedef std::list<ItemKeywordBase *> ItemKeywordSeq;
172         typedef std::list<ItemCreatorBase *> ItemCreatorSeq;
173
174         ItemMap items;
175         ItemKeywordSeq keywords;
176         ItemCreatorSeq creators;
177
178         Collection(const Collection &);
179         Collection &operator=(const Collection &);
180 public:
181         Collection() { }
182         virtual ~Collection();
183
184         /**
185         Adds an object into the collection.  If a name collision occurs, an
186         exception is thrown.  The collection takes ownership of the object.
187         */
188         template<typename T>
189         void add(const std::string &name, T *d)
190         {
191                 if(items.count(name))
192                         throw KeyError("Duplicate key '"+name+"' in collection");
193
194                 items[name]=new Item<typename RemoveConst<T>::Type>(d);
195         }
196
197         /**
198         Gets an object of a specific type from the collection.
199         */
200         template<typename T>
201         T &get(const std::string &name) const
202         {
203                 typedef typename RemoveConst<T>::Type NCT;
204
205                 ItemMap::const_iterator i=items.find(name);
206                 if(i==items.end())
207                         throw KeyError("Item '"+name+"' not found in collection");
208
209                 const Item<NCT> *item=dynamic_cast<const Item<NCT> *>(i->second);
210                 if(!item)
211                         throw TypeError("Item '"+name+"' is not of correct type");
212
213                 return *item->data;
214         }
215
216         /**
217         Gets an object of a specific type from the collection.  If the name is not
218         found in the collection and there is a creator for the item type, it is
219         invoked.
220         */
221         template<typename T>
222         T &get(const std::string &name)
223         {
224                 typedef typename RemoveConst<T>::Type NCT;
225
226                 ItemMap::const_iterator i=items.find(name);
227                 if(i==items.end())
228                 {
229                         for(ItemCreatorSeq::iterator j=creators.begin(); j!=creators.end(); ++j)
230                                 if(NCT *d=(*j)->create<NCT>(*this, name))
231                                 {
232                                         // We already know that the item didn't exist yet
233                                         items[name]=new Item<NCT>(d);
234                                         return *d;
235                                 }
236                         throw KeyError("Item '"+name+"' not found in collection");
237                 }
238
239                 const Item<NCT> *item=dynamic_cast<const Item<NCT> *>(i->second);
240                 if(!item)
241                         throw TypeError("Item '"+name+"' is not of correct type");
242
243                 return *item->data;
244         }
245
246         /**
247         Returns a list of the names of objects of a specific type in the collection.
248         */
249         template<typename T>
250         std::list<std::string> get_names() const
251         {
252                 std::list<std::string> result;
253                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
254                         if(dynamic_cast<const Item<T> *>(i->second))
255                                 result.push_back(i->first);
256                 return result;
257         }
258
259         /**
260         Checks whether a name exists in the collection.  Does not care about the
261         type of the object.
262         */
263         bool contains(const std::string &n) const;
264
265 protected:
266         /**
267         Adds a type that can be loaded from datafiles.
268         */
269         template<typename T>
270         void add_keyword(const std::string &keyword)
271         { add_keyword<T, T>(keyword); }
272
273         /**
274         Adds a type that can be loaded from datafiles, with different storage type.
275         */
276         template<typename T, typename S>
277         void add_keyword(const std::string &keyword)
278         { keywords.push_back(new ItemKeyword<T, S>(keyword)); }
279
280         /**
281         Adds a type that can be created automatically.
282         */
283         template<typename T, typename C>
284         void add_creator(T *(C::*func)(const std::string &))
285         { add_creator<T, T, C>(func); }
286
287         template<typename T, typename S, typename C>
288         void add_creator(T *(C::*func)(const std::string &))
289         { creators.push_back(new ItemCreator<T, S, C>(func)); }
290 };
291
292 } // namespace DataFile
293 } // namespace Msp
294
295 #endif