]> git.tdb.fi Git - libs/game.git/commitdiff
Add a tool for generating setup structs and loaders for them
authorMikko Rasa <tdb@tdb.fi>
Sat, 17 Dec 2022 12:17:08 +0000 (14:17 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sat, 17 Dec 2022 22:17:03 +0000 (00:17 +0200)
.gitignore
Build
tools/setupgen/enum.cpp [new file with mode: 0644]
tools/setupgen/enum.h [new file with mode: 0644]
tools/setupgen/setupgen.cpp [new file with mode: 0644]
tools/setupgen/setupgen.h [new file with mode: 0644]
tools/setupgen/struct.cpp [new file with mode: 0644]
tools/setupgen/struct.h [new file with mode: 0644]
tools/setupgen/type.cpp [new file with mode: 0644]
tools/setupgen/type.h [new file with mode: 0644]

index ecc88cd16ece4f3f7a9a9eb5926cf6338534f58c..6816e2d6531ee6166888df28f871e3434108af98 100644 (file)
@@ -5,3 +5,4 @@ temp
 /libmspgameview.a
 /libmspgameview.so
 /mspgame.pc
+/mspgame-setupgen
diff --git a/Build b/Build
index 15fb6f7d2ebed10c1bed9f1f1100ecd45e96bdf7..7548a1a1e678eccf3e3d7c48fac384875edbc960 100644 (file)
--- a/Build
+++ b/Build
@@ -5,8 +5,6 @@ package "mspgame"
        require "mspcore";
        require "mspdatafile";
        require "mspmath";
-       require "mspgui";
-       require "mspgl";
        require "sigc++-2.0";
 
        build_info
@@ -28,10 +26,18 @@ package "mspgame"
        {
                source "source/gameview";
                use "mspgame";
+               require "mspgui";
+               require "mspgl";
                install true;
                install_map
                {
                        map "source" "include/msp";
                };
        };
+
+       program "mspgame-setupgen"
+       {
+               source "tools/setupgen";
+               install true;
+       };
 };
diff --git a/tools/setupgen/enum.cpp b/tools/setupgen/enum.cpp
new file mode 100644 (file)
index 0000000..b6a40c2
--- /dev/null
@@ -0,0 +1,76 @@
+#include "enum.h"
+#include <msp/io/print.h>
+
+using namespace std;
+using namespace Msp;
+
+Enum::Enum(const string &n):
+       name(n)
+{ }
+
+void Enum::define_type(IO::Base &out) const
+{
+       IO::print(out, "enum class %s\n{\n", name);
+       for(auto i=values.begin();; )
+       {
+               IO::print(out, "\t%s", i->name);
+               if(i->value)
+                       IO::print(out, " = %d", i->value.value());
+               ++i;
+               if(i==values.end())
+                       break;
+               IO::print(out, ",\n");
+       }
+       IO::print(out, "\n};\n");
+}
+
+void Enum::declare_conversions(IO::Base &out) const
+{
+       IO::print(out, "void operator>>(const Msp::LexicalConverter &, %s &);\n", name);
+       IO::print(out, "void operator<<(Msp::LexicalConverter &, %s);\n", name);
+}
+
+void Enum::define_conversions(IO::Base &out) const
+{
+       IO::print(out, "void operator>>(const Msp::LexicalConverter &conv, %s &_e)\n{\n", name);
+       IO::print(out, "\tconst std::string &str = conv.get();\n\t");
+       for(auto i=values.begin(); i!=values.end(); ++i)
+       {
+               if(i!=values.begin())
+                       IO::print(out, "else ");
+               IO::print(out, "if(str==\"%s\")\n\t\t_e = %s::%s;\n\t", i->name, name, i->name);
+       }
+       IO::print(out, "else\n\t\tthrow Msp::lexical_error(Msp::format(\"conversion of '%%s' to %s\", str));\n", name);
+       IO::print(out, "}\n");
+
+       IO::print(out, "\nvoid operator<<(Msp::LexicalConverter &conv, %s _e)\n{\n", name);
+       IO::print(out, "\tswitch(_e)\n\t{\n");
+       for(const Value &v: values)
+               IO::print(out, "\tcase %s::%s: conv.result(\"%s\"); break;\n", name, v.name, v.name);
+       IO::print(out, "\tdefault: conv.result(Msp::format(\"%s(%%#x)\", static_cast<int>(_e)));\n", name);
+       IO::print(out, "\t}\n}\n");
+}
+
+
+Enum::Loader::Loader(Enum &e):
+       ObjectLoader<Enum>(e)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void Enum::Loader::init_actions()
+{
+       add("value", &Loader::value);
+       add("value", &Loader::value_auto);
+}
+
+void Enum::Loader::value(const DataFile::Symbol &n, int v)
+{
+       obj.values.emplace_back(n.name, v);
+}
+
+void Enum::Loader::value_auto(const DataFile::Symbol &n)
+{
+       obj.values.emplace_back(n.name);
+}
diff --git a/tools/setupgen/enum.h b/tools/setupgen/enum.h
new file mode 100644 (file)
index 0000000..26723b2
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef ENUM_H_
+#define ENUM_H_
+
+#include <optional>
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include <msp/io/base.h>
+
+class Enum
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<Enum>
+       {
+       public:
+               Loader(Enum &);
+
+       private:
+               void init_actions() override;
+
+               void value(const Msp::DataFile::Symbol &, int);
+               void value_auto(const Msp::DataFile::Symbol &);
+       };
+
+       struct Value
+       {
+               std::string name;
+               std::optional<int> value;
+       };
+
+private:
+       std::string name;
+       std::vector<Value> values;
+
+public:
+       Enum(const std::string &);
+
+       void define_type(Msp::IO::Base &) const;
+       void declare_conversions(Msp::IO::Base &) const;
+       void define_conversions(Msp::IO::Base &) const;
+};
+
+#endif
diff --git a/tools/setupgen/setupgen.cpp b/tools/setupgen/setupgen.cpp
new file mode 100644 (file)
index 0000000..9506677
--- /dev/null
@@ -0,0 +1,214 @@
+#include "setupgen.h"
+#include <msp/core/getopt.h>
+#include <msp/core/maputils.h>
+#include <msp/fs/utils.h>
+#include <msp/io/console.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+
+using namespace std;
+using namespace Msp;
+
+SetupGen::SetupGen(int argc, char **argv)
+{
+       GetOpt getopt;
+       getopt.add_option('o', "output", out_fn, GetOpt::REQUIRED_ARG);
+       getopt.add_argument("input_file", in_fn, GetOpt::REQUIRED_ARG);
+       getopt(argc, argv);
+}
+
+int SetupGen::main()
+{
+       create_standard_types();
+       load();
+
+       collect_headers();
+
+       if(out_fn.empty())
+       {
+               generate_header(IO::cout);
+               generate_code(IO::cout);
+       }
+       else
+       {
+               string out_base = FS::basename(out_fn);
+               string out_ext = FS::extpart(out_base);
+               if(out_ext==".cpp" || out_ext==".h")
+               {
+                       FS::Path out_dir = FS::dirname(out_fn);
+                       out_base = FS::basepart(out_base);
+                       IO::BufferedFile header_out((out_dir/(out_base+".h")).str(), IO::M_WRITE);
+                       generate_header(header_out);
+                       IO::BufferedFile code_out((out_dir/(out_base+".cpp")).str(), IO::M_WRITE);
+                       IO::print(code_out, "#include \"%s.h\"\n", out_base);
+                       if(!enums.empty())
+                               IO::print(code_out, "#include <msp/strings/format.h>\n");
+                       generate_code(code_out);
+               }
+               else
+               {
+                       IO::BufferedFile out(out_fn, IO::M_WRITE);
+                       generate_header(out);
+                       out.put('\n');
+                       generate_code(out);
+               }
+       }
+
+       return 0;
+}
+
+void SetupGen::create_standard_types()
+{
+       add_type("bool", Type::VALUE);
+       add_type("int", Type::VALUE);
+       add_type("uint", Type::VALUE).set_cpp_type("unsigned");
+       const Type &float_type = add_type("float", Type::VALUE);
+       add_type("string", Type::VALUE).set_cpp_type("std::string", "string");
+       add_type("angle", Type::VALUE).set_cpp_type("Msp::Geometry::Angle<float>", "msp/geometry/angle.h")
+               .set_load("float", "Msp::Geometry::Angle<float>::from_degrees");
+       add_type("vector2", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 2>", "msp/linal/vector.h")
+               .set_elements(float_type, 2);
+       add_type("vector3", Type::AGGREGATE).set_cpp_type("Msp::LinAl::Vector<float, 3>", "msp/linal/vector.h")
+               .set_elements(float_type, 3);
+}
+
+Type &SetupGen::add_type(const string &n, Type::Kind k)
+{
+       auto result = types.try_emplace(n, n, k);
+       if(!result.second)
+               throw key_error(n);
+       return result.first->second;
+}
+
+const Type &SetupGen::get_type(const string &n) const
+{
+       return get_item(types, n);
+}
+
+void SetupGen::load()
+{
+       IO::BufferedFile in_file(in_fn);
+       DataFile::Parser parser(in_file, in_fn);
+       Loader ldr(*this);
+       ldr.load(parser);
+}
+
+void SetupGen::collect_headers()
+{
+       headers.insert("msp/datafile/objectloader.h");
+       headers.insert("msp/strings/lexicalcast.h");
+       for(const unique_ptr<Struct> &s: structs)
+               for(const Struct::Field &f: s->get_fields())
+                       if(const string &h = f.type->get_header(); !h.empty())
+                               headers.insert(h);
+}
+
+void SetupGen::generate_header(IO::Base &out) const
+{
+       string guard = toupper(FS::basepart(FS::basename(out_fn.empty() ? in_fn : out_fn)))+"_H_";
+       if(!name_space.empty())
+       {
+               vector<string> parts = split(name_space, "::");
+               parts.emplace_back(move(guard));
+               guard = join(parts.begin(), parts.end(), "_");
+       }
+       IO::print(out, "#ifndef %s\n", guard);
+       IO::print(out, "#define %s\n", guard);
+
+       if(!headers.empty())
+       {
+               out.put('\n');
+               for(const string &h: headers)
+                       IO::print(out, "#include <%s>\n", h);
+       }
+
+       if(!name_space.empty())
+               IO::print(out, "\nnamespace %s {\n", name_space);
+
+       for(const unique_ptr<Enum> &e: enums)
+       {
+               out.put('\n');
+               e->define_type(out);
+       }
+
+       for(const unique_ptr<Struct> &s: structs)
+       {
+               out.put('\n');
+               s->define_type(out);
+       }
+
+       for(const unique_ptr<Struct> &s: structs)
+       {
+               out.put('\n');
+               s->define_loader(out);
+       }
+
+       for(const unique_ptr<Enum> &e: enums)
+       {
+               out.put('\n');
+               e->declare_conversions(out);
+       }
+
+       if(!name_space.empty())
+               IO::print(out, "\n}  // namespace %s\n", name_space);
+
+       IO::print(out, "\n#endif\n");
+}
+
+void SetupGen::generate_code(IO::Base &out) const
+{
+       if(!name_space.empty())
+               IO::print(out, "\nnamespace %s {\n", name_space);
+
+       for(const unique_ptr<Struct> &s: structs)
+       {
+               out.put('\n');
+               s->define_functions(out);
+       }
+
+       for(const unique_ptr<Enum> &e: enums)
+       {
+               out.put('\n');
+               e->define_conversions(out);
+       }
+
+       if(!name_space.empty())
+               IO::print(out, "\n}  // namespace %s\n", name_space);
+}
+
+
+SetupGen::Loader::Loader(SetupGen &s):
+       ObjectLoader<SetupGen>(s)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void SetupGen::Loader::init_actions()
+{
+       add("component", &Loader::struct_def, Struct::COMPONENT);
+       add("entity", &Loader::struct_def, Struct::ENTITY);
+       add("enum", &Loader::enum_def);
+       add("namespace", &Loader::name_space);
+}
+
+void SetupGen::Loader::enum_def(const DataFile::Symbol &n)
+{
+       Enum en(n.name);
+       load_sub(en);
+       Type &type = obj.add_type(n.name, Type::ENUM);
+       type.set_enum(*obj.enums.emplace_back(make_unique<Enum>(move(en))));
+}
+
+void SetupGen::Loader::name_space(const string &ns)
+{
+       obj.name_space = ns;
+}
+
+void SetupGen::Loader::struct_def(Struct::Kind kind, const DataFile::Symbol &n)
+{
+       Struct sct(n.name+"Setup", kind);
+       load_sub(sct, obj);
+       Type &type = obj.add_type(n.name, Type::STRUCT);
+       type.set_struct(*obj.structs.emplace_back(make_unique<Struct>(move(sct))));
+}
diff --git a/tools/setupgen/setupgen.h b/tools/setupgen/setupgen.h
new file mode 100644 (file)
index 0000000..98d1ab8
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef SETUPGEN_H_
+#define SETUPGEN_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <msp/core/application.h>
+#include <msp/datafile/objectloader.h>
+#include "enum.h"
+#include "struct.h"
+#include "type.h"
+
+class SetupGen: public Msp::RegisteredApplication<SetupGen>
+{
+private:
+       class Loader: public Msp::DataFile::ObjectLoader<SetupGen>
+       {
+       public:
+               Loader(SetupGen &);
+
+       private:
+               void init_actions() override;
+
+               void enum_def(const Msp::DataFile::Symbol &);
+               void name_space(const std::string &);
+               void struct_def(Struct::Kind, const Msp::DataFile::Symbol &);
+       };
+
+       std::string in_fn;
+       std::string out_fn;
+       std::string name_space;
+       std::map<std::string, Type> types;
+       std::vector<std::unique_ptr<Enum>> enums;
+       std::vector<std::unique_ptr<Struct>> structs;
+       std::set<std::string> headers;
+
+public:
+       SetupGen(int, char **);
+
+       int main() override;
+private:
+       void create_standard_types();
+       Type &add_type(const std::string &, Type::Kind);
+public:
+       const Type &get_type(const std::string &) const;
+
+private:
+       void load();
+       void collect_headers();
+       void generate_header(Msp::IO::Base &) const;
+       void generate_code(Msp::IO::Base &) const;
+};
+
+#endif
diff --git a/tools/setupgen/struct.cpp b/tools/setupgen/struct.cpp
new file mode 100644 (file)
index 0000000..b93b5be
--- /dev/null
@@ -0,0 +1,128 @@
+#include "struct.h"
+#include <algorithm>
+#include <msp/core/maputils.h>
+#include <msp/io/print.h>
+#include "setupgen.h"
+
+using namespace std;
+using namespace Msp;
+
+class Struct::Field::Loader: public Msp::DataFile::ObjectLoader<Struct::Field>
+{
+public:
+       Loader(Struct::Field &);
+
+private:
+       void init_actions() override;
+};
+
+
+Struct::Struct(const string &n, Kind k):
+       name(n),
+       kind(k)
+{ }
+
+void Struct::define_type(IO::Base &out) const
+{
+       IO::print(out, "struct %s\n{\n", name);
+       IO::print(out, "\tclass Loader;\n\n");
+
+       for(const Field &f: fields)
+       {
+               IO::print(out, "\t%s %s", f.type->get_cpp_type(), f.name);
+               if(!f.default_value.empty())
+               {
+                       if(const string &c = f.type->get_conversion(); !c.empty())
+                               IO::print(out, " = %s(%s)", f.type->get_conversion(), f.default_value);
+                       else if(f.type->get_kind() == Type::AGGREGATE)
+                               IO::print(out, " = { %s }", f.default_value);
+                       else if(f.type->get_kind() == Type::ENUM)
+                               IO::print(out, " = %s::%s", f.type->get_name(), f.default_value);
+                       else
+                               IO::print(out, " = %s", f.default_value);
+               }
+               IO::print(out, ";\n");
+       }
+
+       IO::print(out, "};\n");
+}
+
+void Struct::define_loader(IO::Base &out) const
+{
+       IO::print(out, "class %s::Loader: public Msp::DataFile::ObjectLoader<%s>\n{\n", name, name);
+       IO::print(out, "public:\n");
+       IO::print(out, "\tLoader(%s &);\n", name);
+       IO::print(out, "\nprivate:\n");
+       IO::print(out, "\tvoid init_actions() override;\n");
+       for(const Field &f: fields)
+               if(f.type->needs_loader_function())
+                       IO::print(out, "\tvoid %s(%s);\n", f.name, f.type->create_loader_params(false));
+       IO::print(out, "};\n");
+}
+
+void Struct::define_functions(IO::Base &out) const
+{
+       IO::print(out, "%s::Loader::Loader(%s &o):\n", name, name);
+       IO::print(out, "\tObjectLoader<%s>(o)\n{\n", name);
+       IO::print(out, "\tstatic ActionMap shared_actions;\n");
+       IO::print(out, "\tset_actions(shared_actions);\n}\n");
+
+       IO::print(out, "\nvoid %s::Loader::init_actions()\n{\n", name);
+       for(const Field &f: fields)
+       {
+               IO::print(out, "\tadd(\"%s\", ", f.name);
+               if(f.type->needs_loader_function())
+                       IO::print(out, "&Loader::%s", f.name);
+               else
+                       IO::print(out, "&%s::%s", name, f.name);
+               IO::print(out, ");\n");
+       }
+       IO::print(out, "}\n");
+
+       for(const Field &f: fields)
+               if(f.type->needs_loader_function())
+               {
+                       IO::print(out, "\nvoid %s::Loader::%s(%s)\n{\n", name, f.name, f.type->create_loader_params(true));
+                       IO::print(out, "\tauto &_v = obj.%s;\n", f.name);
+                       IO::print(out, "\t%s\n", f.type->create_loader_statement());
+                       IO::print(out, "}\n");
+               }
+}
+
+
+Struct::Loader::Loader(Struct &s, const SetupGen &g):
+       ObjectLoader<Struct>(s),
+       gen(g)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void Struct::Loader::init_actions()
+{
+       add("field", &Loader::field);
+}
+
+void Struct::Loader::field(const DataFile::Symbol &n, const DataFile::Symbol &t)
+{
+       if(ranges::any_of(obj.fields, [&n](const Field &f){ return f.name==n.name; }))
+               throw key_error(n.name);
+       Field fld;
+       fld.name=n.name;
+       fld.type=&gen.get_type(t.name);
+       load_sub(fld);
+       obj.fields.emplace_back(move(fld));
+}
+
+
+Struct::Field::Loader::Loader(Field &f):
+       ObjectLoader<Field>(f)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void Struct::Field::Loader::init_actions()
+{
+       add("default", &Field::default_value);
+}
diff --git a/tools/setupgen/struct.h b/tools/setupgen/struct.h
new file mode 100644 (file)
index 0000000..adc95f8
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef STRUCT_H_
+#define STRUCT_H_
+
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include <msp/io/base.h>
+
+class SetupGen;
+class Type;
+
+class Struct
+{
+public:
+       enum Kind
+       {
+               DATA,
+               ENTITY,
+               COMPONENT
+       };
+
+       struct Field
+       {
+               class Loader;
+
+               std::string name;
+               const Type *type = nullptr;
+               std::string default_value;
+       };
+
+       class Loader;
+
+private:
+       std::string name;
+       Kind kind;
+       std::vector<Field> fields;
+
+public:
+       Struct(const std::string &, Kind);
+
+       const std::vector<Field> &get_fields() const { return fields; }
+
+       void define_type(Msp::IO::Base &) const;
+       void define_loader(Msp::IO::Base &) const;
+       void define_functions(Msp::IO::Base &) const;
+};
+
+class Struct::Loader: public Msp::DataFile::ObjectLoader<Struct>
+{
+private:
+       const SetupGen &gen;
+
+public:
+       Loader(Struct &, const SetupGen &);
+
+private:
+       void init_actions() override;
+
+       void field(const Msp::DataFile::Symbol &, const Msp::DataFile::Symbol &);
+};
+
+#endif
diff --git a/tools/setupgen/type.cpp b/tools/setupgen/type.cpp
new file mode 100644 (file)
index 0000000..4eb19da
--- /dev/null
@@ -0,0 +1,171 @@
+#include "type.h"
+#include <msp/core/except.h>
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+
+using namespace std;
+using namespace Msp;
+
+Type::Type(const string &n, Kind k):
+       name(n),
+       kind(k)
+{
+       if(kind==VALUE || kind==ENUM)
+               cpp_type = name;
+       if(kind==STRUCT)
+               cpp_type = name+"Setup";
+}
+
+Type &Type::set_cpp_type(const string &t, const string &h)
+{
+       cpp_type = t;
+       header = h;
+       return *this;
+}
+
+Type &Type::set_elements(const Type &t, unsigned c)
+{
+       if(kind!=AGGREGATE && kind!=POINTER && kind!=DYN_ARRAY)
+               throw invalid_state("wrong kind");
+       if((kind==POINTER && c>1) || (kind==DYN_ARRAY && c>0))
+               throw invalid_argument("Type::set_elements");
+
+       element_type = &t;
+       element_count = (kind==POINTER ? 1 : c);
+       if(cpp_type.empty())
+       {
+               if(kind==POINTER)
+                       cpp_type = format("%s *", t.get_cpp_type());
+               else if(kind==DYN_ARRAY)
+                       cpp_type = format("std::vector<%s>", t.get_cpp_type());
+       }
+
+       return *this;
+}
+
+Type &Type::set_struct(const Struct &s)
+{
+       if(kind!=STRUCT)
+               throw invalid_state("wrong kind");
+       struct_def = &s;
+       return *this;
+}
+
+Type &Type::set_enum(const Enum &e)
+{
+       if(kind!=ENUM)
+               throw invalid_state("wrong kind");
+       enum_def = &e;
+       return *this;
+}
+
+Type &Type::set_load(const string &l, const string &c)
+{
+       if(kind!=VALUE)
+               throw invalid_state("wrong kind");
+       load_type = l;
+       conversion = c;
+       return *this;
+}
+
+const Type &Type::get_element_type() const
+{
+       if(kind!=AGGREGATE && kind!=POINTER && kind!=DYN_ARRAY)
+               throw invalid_state("wrong kind");
+       if(!element_type)
+               throw invalid_state("no element type");
+       return *element_type;
+}
+
+const Struct &Type::get_struct() const
+{
+       if(kind!=STRUCT)
+               throw invalid_state("wrong kind");
+       if(!struct_def)
+               throw invalid_state("no struct");
+       return *struct_def;
+}
+
+const Enum &Type::get_enum() const
+{
+       if(kind!=ENUM)
+               throw invalid_state("wrong kind");
+       if(!enum_def)
+               throw invalid_state("no enum");
+       return *enum_def;
+}
+
+bool Type::needs_loader_function() const
+{
+       switch(kind)
+       {
+       case VALUE: return !load_type.empty();
+       case POINTER:
+       case ENUM: return false;
+       case AGGREGATE:
+       case DYN_ARRAY:
+       case STRUCT: return true;
+       default: throw invalid_state("bad kind");
+       }
+}
+
+string Type::create_loader_params(bool with_names) const
+{
+       if(kind==AGGREGATE)
+       {
+               string result;
+               for(unsigned i=0; i<element_count; ++i)
+               {
+                       if(with_names)
+                               append(result, ", ", format("%s _a%d", element_type->get_cpp_type(), i));
+                       else
+                               append(result, ", ", element_type->get_cpp_type());
+               }
+               return result;
+       }
+       else if(kind==VALUE)
+       {
+               const string &type = (load_type.empty() ? cpp_type : load_type);
+               return (with_names ? format("%s _a", type) : type);
+       }
+       else if(kind==DYN_ARRAY)
+       {
+               Kind elem_kind = element_type->get_kind();
+               if(elem_kind==VALUE || elem_kind==ENUM)
+               {
+                       string type = format("std::vector<%s>", element_type->get_cpp_type());
+                       return (with_names ? format("%s _a", type) : type);
+               }
+               else
+                       return string();
+       }
+       else if(kind==STRUCT)
+               return string();
+       else
+               throw invalid_state("wrong kind");
+}
+
+string Type::create_loader_statement() const
+{
+       if(kind==AGGREGATE)
+       {
+               string init;
+               for(unsigned i=0; i<element_count; ++i)
+                       append(init, ", ", format("_a%d", i));
+               return format("_v = { %s };", init);
+       }
+       else if(kind==VALUE)
+               return format("_v = %s(_a);", (conversion.empty() ? cpp_type : conversion));
+       else if(kind==STRUCT)
+               return "load_sub(_v);";
+       else if(kind==DYN_ARRAY)
+       {
+               Kind elem_kind = element_type->get_kind();
+               if(elem_kind==VALUE || elem_kind==ENUM)
+                       return "_v.insert(_v.end(), _a.begin(), _a.end());";
+               else
+                       return format("%s _e; load_sub(_e); _v.emplace_back(move(_e));", cpp_type);
+       }
+       else
+               throw invalid_state("wrong kind");
+}
diff --git a/tools/setupgen/type.h b/tools/setupgen/type.h
new file mode 100644 (file)
index 0000000..3338fd9
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef TYPE_H_
+#define TYPE_H_
+
+#include <string>
+
+class Enum;
+class Struct;
+
+class Type
+{
+public:
+       enum Kind
+       {
+               VALUE,
+               AGGREGATE,
+               POINTER,
+               DYN_ARRAY,
+               STRUCT,
+               ENUM
+       };
+
+private:
+       std::string name;
+       Kind kind = VALUE;
+       std::string cpp_type;
+       std::string header;
+       const Type *element_type = nullptr;
+       unsigned element_count = 0;
+       std::string load_type;
+       std::string conversion;
+       const Struct *struct_def = nullptr;
+       const Enum *enum_def = nullptr;
+
+public:
+       Type(const std::string &, Kind);
+
+       Type &set_cpp_type(const std::string &, const std::string & = std::string());
+       Type &set_elements(const Type &, unsigned = 0);
+       Type &set_struct(const Struct &);
+       Type &set_enum(const Enum &);
+       Type &set_load(const std::string &, const std::string & = std::string());
+
+       const std::string &get_name() const { return name; }
+       Kind get_kind() const { return kind; }
+       const std::string &get_cpp_type() const { return cpp_type; }
+       const std::string &get_header() const { return header; }
+       const Type &get_element_type() const;
+       const std::string &get_conversion() const { return conversion; }
+       const Struct &get_struct() const;
+       const Enum &get_enum() const;
+
+       bool needs_loader_function() const;
+       std::string create_loader_params(bool) const;
+       std::string create_loader_statement() const;
+};
+
+#endif