From: Mikko Rasa Date: Tue, 12 Dec 2023 09:32:21 +0000 (+0200) Subject: Introduce an improved interface for loading sub-objects X-Git-Url: https://git.tdb.fi/?a=commitdiff_plain;h=ef84d2328cb05de6703ec66b38a7a14f96bb765d;p=libs%2Fdatafile.git Introduce an improved interface for loading sub-objects Objects can now be created dynamically and stored in a collection. Hierarchical names are automatically generated for stored objects. The old load_sub function will be deprecated at some point. --- diff --git a/source/collection.h b/source/collection.h index ad66523..45dd103 100644 --- a/source/collection.h +++ b/source/collection.h @@ -71,13 +71,7 @@ public: Collection &get_object() const { return coll; } private: template - void item(const std::string &n) - { - std::unique_ptr it = std::make_unique(); - ItemLoader ldr(*it, coll); - load_sub_with(ldr); - coll.add(n, std::move(it)); - } + void item(const std::string &n) { make_sub().into(coll, n).load(); } }; template::value> diff --git a/source/dynamicobjectloader.h b/source/dynamicobjectloader.h index 2cf1b36..022f07e 100644 --- a/source/dynamicobjectloader.h +++ b/source/dynamicobjectloader.h @@ -20,6 +20,7 @@ template class DynamicObjectLoader: public Loader { public: + using Object = T; using Collection = C; protected: diff --git a/source/loader.cpp b/source/loader.cpp index afd803b..81883d7 100644 --- a/source/loader.cpp +++ b/source/loader.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "except.h" #include "loader.h" @@ -53,6 +54,8 @@ void Loader::load(Parser &p) if(!actions) throw logic_error("no actions"); + context = FS::basename(p.get_source()); + while(p) { if(p.peek(0)) diff --git a/source/loader.h b/source/loader.h index 0cab090..5b7b442 100644 --- a/source/loader.h +++ b/source/loader.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include "loaderaction.h" @@ -41,9 +42,43 @@ class MSPDATAFILE_API Loader: private NonCopyable protected: using ActionMap = std::map>; + template + class SubOps: private NonCopyable + { + public: + using ObjType = typename L::Object; + using LoadResult = std::conditional_t, + std::conditional_t, std::unique_ptr, P>, + ObjType *>; + + private: + Loader &parent; + P obj; + C *coll = nullptr; + std::string name_; + + public: + SubOps(Loader &p, P o): parent(p), obj(std::move(o)) { } + SubOps(Loader &p, P o, C *c, const std::string &n): parent(p), obj(std::move(o)), coll(c) { name(n); } + + SubOps &name(const std::string &n) { name_ = parent.context+": "+n; return *this; } + + /** Stores the object in a collection after loading. The object must be + created by the loader. */ + template + SubOps into(C2 &c, const std::string &n) { return { parent, std::move(obj), &c, n }; } + + /** Loads the object. Arguments are passed to the loader's constructor. + If into() was called, a reference to the collection will be passed as + well. */ + template + LoadResult load(Args &&...); + }; + private: ActionMap local_actions; ActionMap *actions = nullptr; + std::string context; Parser *cur_parser = nullptr; unsigned cur_level = 0; const Statement *cur_st = nullptr; @@ -72,27 +107,38 @@ private: protected: /** Loads a sub-object from the statement being processed. The Loader class - of the sub-object is automatically used. */ - template - void load_sub(S &s) - { - typename S::Loader ldr(s); - load_sub_with(ldr); - } - - /** Loads a sub-object from the statement being processed with an extra - arguments for the Loader. The Loader class of the sub-object is - automatically used. */ + of the sub-object is automatically used and any extra arguments are passed + to its constructor. */ template void load_sub(S &s, Args &&... args) - { - typename S::Loader ldr(s, std::forward(args)...); - load_sub_with(ldr); - } + { sub(s).load(std::forward(args)...); } /** Processes the current statement's substatements with another Loader. */ void load_sub_with(Loader &); + /** Specifies operations for loading an existing sub-object. The load call + will return a pointer to the object. */ + template + SubOps sub(S &s) + { return { *this, &s }; } + + /** Specifies operations for creating a sub-object and loading it. The type + of the object must be given as an explicit template argument. Function + arguments are passed to the object's constructor. The load call will return + a raw pointer if the object was stored in a collection, or a unique pointer + otherwise. */ + template + SubOps, typename S::Loader> make_sub(Args &&... args) + { return { *this, std::make_unique(std::forward(args)...) }; } + + /** Specifies operations for loading a dynamically-typed subobject. An + explicit template argument naming a DynamicObjectLoader subclass must be + given. The load call will return a raw pointer if the object was stored in + a collection, or a unique pointer otherwise.*/ + template + SubOps dyn_sub() + { return { *this, nullptr }; } + /** Sets the actions to be used when loading. If the map is empty, init_actions will be called. */ void set_actions(ActionMap &); @@ -160,6 +206,44 @@ protected: }; +template +template +typename Loader::SubOps::LoadResult Loader::SubOps::load(Args &&... args) +{ + std::optional ldr; + if constexpr(std::is_same_v) + { + if constexpr(!std::is_same_v && NeedsCollection::value) + ldr.emplace(*coll, std::forward(args)...); + else + ldr.emplace(std::forward(args)...); + } + else if constexpr(!std::is_same_v && NeedsCollection::value) + ldr.emplace(*obj, *coll, std::forward(args)...); + else + ldr.emplace(*obj, std::forward(args)...); + + ldr->context = name_; + parent.load_sub_with(*ldr); + + if constexpr(std::is_same_v) + { + if constexpr(std::is_same_v) + return ldr->get_object(); + else + return ldr->store_object(*coll, name_); + } + else if constexpr(std::is_same_v) + return std::move(obj); + else + { + LoadResult result = obj.get(); + coll->add(name_, std::move(obj)); + return result; + } +} + + /** Loads an object from a file. The object must have a public Loader class. Any extra arguments are passed to the Loader constructor.