From: Mikko Rasa Date: Mon, 15 Oct 2007 14:16:41 +0000 (+0000) Subject: Add Collection X-Git-Tag: 1.0~17 X-Git-Url: http://git.tdb.fi/?p=libs%2Fdatafile.git;a=commitdiff_plain;h=1d9c21a8a301007fb242e05b69cc6390ec566273 Add Collection Support for loading pointers --- diff --git a/source/collection.cpp b/source/collection.cpp new file mode 100644 index 0000000..ea1095b --- /dev/null +++ b/source/collection.cpp @@ -0,0 +1,37 @@ +/* $Id$ + +This file is part of libmspdatafile +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#include "collection.h" + +namespace Msp { +namespace DataFile { + +Collection::~Collection() +{ + for(ItemMap::iterator i=items.begin(); i!=items.end(); ++i) + delete i->second; + for(ItemKeywordSeq::iterator i=keywords.begin(); i!=keywords.end(); ++i) + delete *i; + for(ItemCreatorSeq::iterator i=creators.begin(); i!=creators.end(); ++i) + delete *i; +} + +bool Collection::contains(const std::string &n) const +{ + return items.count(n); +} + + +Collection::Loader::Loader(Collection &c): + coll(c) +{ + for(ItemKeywordSeq::const_iterator i=coll.keywords.begin(); i!=coll.keywords.end(); ++i) + (*i)->add_to_loader(*this); +} + +} // namespace DataFile +} // namespace Msp diff --git a/source/collection.h b/source/collection.h new file mode 100644 index 0000000..e7d173d --- /dev/null +++ b/source/collection.h @@ -0,0 +1,281 @@ +/* $Id$ + +This file is part of libmspdatafile +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ + +#ifndef MSP_DATAFILE_COLLECTION_H_ +#define MSP_DATAFILE_COLLECTION_H_ + +#include +#include "item.h" +#include "loader.h" + +namespace Msp { +namespace DataFile { + +/** +Helper struct to determine whether a Loader has a Collection typedef. +*/ +template +struct NeedsCollection +{ + struct Yes { char c[2]; }; + struct No { char c; }; + + template + static Yes f(typename U::Collection *); + template + static No f(...); + + enum { result=(sizeof(f(0))==sizeof(Yes)) }; +}; + +/** +A collection of objects that can be loaded from a datafile. Each object is +identified by a name, which must be unique across the entire collection. +*/ +class Collection +{ +public: + class Loader; + +private: + /* XXX I don't really like sticking all this stuff in here, but there's some + complex inter-class relationships, especially between ItemKeyword and + Collection::Loader. */ + + struct ItemBase + { + virtual ~ItemBase() { } + }; + + template + struct Item: public ItemBase + { + T *data; + + Item(T *d): data(d) { } + ~Item() { delete data; } + }; + + /** + Used to store keywords for types that can be loaded. + */ + struct ItemKeywordBase + { + virtual void add_to_loader(Loader &) const { }; + }; + + template::result> + struct ItemKeyword: public ItemKeywordBase + { + std::string keyword; + + ItemKeyword(const std::string &kw): keyword(kw) { } + + void add_to_loader(Loader &ldr) const + { ldr.add(keyword, &Loader::item); } + }; + + template + struct ItemKeyword: public ItemKeywordBase + { + std::string keyword; + + ItemKeyword(const std::string &kw): keyword(kw) { } + + virtual void add_to_loader(Loader &ldr) const + { ldr.add(keyword, &Loader::coll_item); } + }; + + /** + Used to store types that can be created automatically. + */ + struct ItemCreatorBase + { + virtual ~ItemCreatorBase() { } + + template + S *create(Collection &coll, const std::string &name) + { + ItemCreatorBridge *creator=dynamic_cast *>(this); + if(creator) + return creator->create(coll, name); + return 0; + } + }; + + template + struct ItemCreatorBridge: public ItemCreatorBase + { + virtual S *create(Collection &, const std::string &) const =0; + }; + + template + struct ItemCreator: public ItemCreatorBridge + { + typedef T *(C::*fCreate)(const std::string &); + + fCreate create_func; + + ItemCreator(fCreate cf): create_func(cf) { } + virtual S *create(Collection &coll, const std::string &name) const + { return (dynamic_cast(coll).*create_func)(name); } + }; + +public: + /** + Loads objects into a Collection. + */ + class Loader: public DataFile::Loader + { + private: + Collection &coll; + + public: + Loader(Collection &); + Collection &get_object() const { return coll; } + private: + template + void coll_item(const std::string &n) + { + RefPtr it=new T; + load_sub(*it, dynamic_cast(coll)); + coll.add(n, it.get()); + it.release(); + } + + template + void item(const std::string &n) + { + RefPtr it=new T; + load_sub(*it); + coll.add(n, it.get()); + it.release(); + } + + template friend class ItemKeyword; + }; + +private: + typedef std::map ItemMap; + typedef std::list ItemKeywordSeq; + typedef std::list ItemCreatorSeq; + + ItemMap items; + ItemKeywordSeq keywords; + ItemCreatorSeq creators; + +public: + virtual ~Collection(); + + /** + Adds an object into the collection. If a name collision occurs, an + exception is thrown. The collection takes ownership of the object. + */ + template + void add(const std::string &name, T *d) + { + if(items.count(name)) + throw KeyError("Duplicate key '"+name+"' in collection"); + + items[name]=new Item(d); + } + + /** + Gets an object of a specific type from the collection. + */ + template + T &get(const std::string &name) const + { + ItemMap::const_iterator i=items.find(name); + if(i==items.end()) + throw KeyError("Item '"+name+"' not found in collection"); + + const Item *item=dynamic_cast *>(i->second); + if(!item) + throw TypeError("Item '"+name+"' is not of correct type"); + + return *item->data; + } + + /** + Gets an object of a specific type from the collection. If the name is not + found in the collection and there is a creator for the item type, it is + invoked. + */ + template + T &get(const std::string &name) + { + ItemMap::const_iterator i=items.find(name); + if(i==items.end()) + { + for(ItemCreatorSeq::iterator j=creators.begin(); j!=creators.end(); ++j) + if(T *d=(*j)->create(*this, name)) + { + // We already know that the item didn't exist yet + items[name]=new Item(d); + return *d; + } + throw KeyError("Item '"+name+"' not found in collection"); + } + + const Item *item=dynamic_cast *>(i->second); + if(!item) + throw TypeError("Item '"+name+"' is not of correct type"); + + return *item->data; + } + + /** + Returns a list of the names of objects of a specific type in the collection. + */ + template + std::list get_names() const + { + std::list result; + for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i) + if(dynamic_cast *>(i->second)) + result.push_back(i->first); + return result; + } + + /** + Checks whether a name exists in the collection. Does not care about the + type of the object. + */ + bool contains(const std::string &n) const; + +protected: + /** + Adds a type that can be loaded from datafiles. + */ + template + void add_keyword(const std::string &keyword) + { add_keyword(keyword); } + + /** + Adds a type that can be loaded from datafiles, with different storage type. + */ + template + void add_keyword(const std::string &keyword) + { keywords.push_back(new ItemKeyword(keyword)); } + + /** + Adds a type that can be created automatically. + */ + template + void add_creator(T *(C::*func)(const std::string &)) + { add_creator(func); } + + template + void add_creator(T *(C::*func)(const std::string &)) + { creators.push_back(new ItemCreator(func)); } +}; + +} // namespace DataFile +} // namespace Msp + +#endif diff --git a/source/loader.h b/source/loader.h index e1522de..da1da3a 100644 --- a/source/loader.h +++ b/source/loader.h @@ -10,7 +10,7 @@ Distributed under the LGPL #include #include -#include "error.h" +#include "except.h" #include "parser.h" #include "statement.h" #include "value.h" @@ -168,6 +168,24 @@ private: }; +template +class LoadValue1: public LoaderAction +{ +public: + typedef T0 *L::*Pointer0Type; + + LoadValue1(Pointer0Type p0): ptr0(p0) { } + void execute(Loader &l, const Statement &st) const + { + if(st.args.size()!=1) throw TypeError(st.get_location()+": Wrong number of arguments"); + typename L::Loader &ldr=dynamic_cast(l); + ldr.get_object().*ptr0=&ldr.get_collection().template get(st.args[0].get()); + } +private: + Pointer0Type ptr0; +}; + + template class LoadValue2: public LoaderAction { @@ -193,7 +211,9 @@ Base class for data loaders. To give loading capabilities to a class, create a public Loader class in it, derived from this class. Typically a loader object contains a reference to the loaded object. To make use of loading directly into data members, the Loader class must have a get_object() member function, -returning that reference. +returning that reference. If direct loading of pointers is desired, the Loader +class must also have a get_collection() member function, returning a collection +to get pointers from. */ class Loader {