#include <memory>
#include <map>
+#include <optional>
#include <type_traits>
#include <msp/io/file.h>
#include "loaderaction.h"
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;
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 &);
};
+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.