]> git.tdb.fi Git - libs/datafile.git/commitdiff
Introduce an improved interface for loading sub-objects
authorMikko Rasa <tdb@tdb.fi>
Tue, 12 Dec 2023 09:32:21 +0000 (11:32 +0200)
committerMikko Rasa <tdb@tdb.fi>
Tue, 12 Dec 2023 10:10:31 +0000 (12:10 +0200)
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.

source/collection.h
source/dynamicobjectloader.h
source/loader.cpp
source/loader.h

index ad665235d94199904e6f63de5b9676c7fb894fe6..45dd103e8e949d43482780ecffea02e75e1b598a 100644 (file)
@@ -71,13 +71,7 @@ public:
                Collection &get_object() const { return coll; }
        private:
                template<typename T, typename S>
-               void item(const std::string &n)
-               {
-                       std::unique_ptr<T> it = std::make_unique<T>();
-                       ItemLoader<T> ldr(*it, coll);
-                       load_sub_with(ldr);
-                       coll.add<S>(n, std::move(it));
-               }
+               void item(const std::string &n) { make_sub<T>().into(coll, n).load(); }
        };
 
        template<typename T, bool = NeedsCollection<typename T::Loader>::value>
index 2cf1b361df3de60fa27f58e22d8283a96a51c609..022f07e19325091af6646d6bc26c6927ec3417c6 100644 (file)
@@ -20,6 +20,7 @@ template<typename T, typename C = Collection>
 class DynamicObjectLoader: public Loader
 {
 public:
+       using Object = T;
        using Collection = C;
 
 protected:
index afd803b21d1fe0661c5ca5ed3c65fcc2a85ccc9c..81883d730781407ee01ad88dcc240cdba0aaf891 100644 (file)
@@ -1,5 +1,6 @@
 #include <msp/core/algorithm.h>
 #include <msp/core/raii.h>
+#include <msp/fs/utils.h>
 #include <msp/strings/format.h>
 #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))
index 0cab090184ec1f06fdece29e548a2625a3b88a3d..5b7b4422ad5ea646e4c0aa5f8f15c5b883fd5e4e 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <memory>
 #include <map>
+#include <optional>
 #include <type_traits>
 #include <msp/io/file.h>
 #include "loaderaction.h"
@@ -41,9 +42,43 @@ class MSPDATAFILE_API Loader: private NonCopyable
 protected:
        using ActionMap = std::map<StatementKey, std::unique_ptr<LoaderAction>>;
 
+       template<typename P, typename L, typename C = void>
+       class SubOps: private NonCopyable
+       {
+       public:
+               using ObjType = typename L::Object;
+               using LoadResult = std::conditional_t<std::is_same_v<C, void>,
+                       std::conditional_t<std::is_same_v<P, nullptr_t>, std::unique_ptr<ObjType>, 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<typename C2>
+               SubOps<P, L, C2> 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<typename... Args>
+               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<typename S>
-       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<typename S, typename... Args>
        void load_sub(S &s, Args &&... args)
-       {
-               typename S::Loader ldr(s, std::forward<Args>(args)...);
-               load_sub_with(ldr);
-       }
+       { sub(s).load(std::forward<Args>(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<typename S>
+       SubOps<S *, typename S::Loader> 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<typename S, typename... Args>
+       SubOps<std::unique_ptr<S>, typename S::Loader> make_sub(Args &&... args)
+       { return { *this, std::make_unique<S>(std::forward<Args>(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<typename L>
+       SubOps<nullptr_t, L> 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<typename P, typename L, typename C>
+template<typename... Args>
+typename Loader::SubOps<P, L, C>::LoadResult Loader::SubOps<P, L, C>::load(Args &&... args)
+{
+       std::optional<L> ldr;
+       if constexpr(std::is_same_v<P, nullptr_t>)
+       {
+               if constexpr(!std::is_same_v<C, void> && NeedsCollection<L>::value)
+                       ldr.emplace(*coll, std::forward<Args>(args)...);
+               else
+                       ldr.emplace(std::forward<Args>(args)...);
+       }
+       else if constexpr(!std::is_same_v<C, void> && NeedsCollection<L>::value)
+               ldr.emplace(*obj, *coll, std::forward<Args>(args)...);
+       else
+               ldr.emplace(*obj, std::forward<Args>(args)...);
+
+       ldr->context = name_;
+       parent.load_sub_with(*ldr);
+
+       if constexpr(std::is_same_v<P, nullptr_t>)
+       {
+               if constexpr(std::is_same_v<C, void>)
+                       return ldr->get_object();
+               else
+                       return ldr->store_object(*coll, name_);
+       }
+       else if constexpr(std::is_same_v<C, void>)
+               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.