diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2021-04-17 21:41:26 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2021-11-07 16:41:37 +0000 |
commit | 71c5280bc6393669777147b5036dc510e74d93c4 (patch) | |
tree | 4efcc41b8d87f2c4a0dff3b6ad852bdaf809f514 | |
parent | Initial commit of basis persistence (diff) | |
download | ilt-71c5280bc6393669777147b5036dc510e74d93c4.tar.bz2 ilt-71c5280bc6393669777147b5036dc510e74d93c4.tar.xz ilt-71c5280bc6393669777147b5036dc510e74d93c4.zip |
Second swing persistance
Mostly functional JSON deserialising for most types.
-rw-r--r-- | Jamroot.jam | 1 | ||||
-rw-r--r-- | lib/jsonParse.impl.cpp | 23 | ||||
-rw-r--r-- | lib/jsonParse.ll | 2 | ||||
-rw-r--r-- | lib/persistance.cpp | 74 | ||||
-rw-r--r-- | lib/persistance.h | 201 | ||||
-rw-r--r-- | test/test-persistance.cpp | 328 |
6 files changed, 399 insertions, 230 deletions
diff --git a/Jamroot.jam b/Jamroot.jam index 11df744..d248313 100644 --- a/Jamroot.jam +++ b/Jamroot.jam @@ -21,6 +21,7 @@ project : requirements <variant>release:<lto>on <variant>coverage:<coverage>on <toolset>tidy:<exclude>gfx/models/obj.cpp + <toolset>tidy:<exclude>lib/jsonParse.cpp <toolset>tidy:<checkxx>boost-* <toolset>tidy:<checkxx>bugprone-* <toolset>tidy:<checkxx>clang-* diff --git a/lib/jsonParse.impl.cpp b/lib/jsonParse.impl.cpp index 27e73b4..3ed8e62 100644 --- a/lib/jsonParse.impl.cpp +++ b/lib/jsonParse.impl.cpp @@ -1,4 +1,7 @@ #include "jsonParse.h" +#include <cstdlib> +#include <stdexcept> +#include <string> void json::jsonParser::LexerError(const char * msg) @@ -16,24 +19,24 @@ void json::jsonParser::appendEscape(unsigned long cp, std::string & str) { if (cp <= 0x7F) { - str += cp; + str += (char)cp; } else if (cp <= 0x7FF) { - str += (cp >> 6) + 192; - str += (cp & 63) + 128; + str += char((cp >> 6) + 192); + str += char((cp & 63) + 128); } else if (0xd800 <= cp && cp <= 0xdfff) { throw std::range_error("Invalid UTF-8 sequence"); } else if (cp <= 0xFFFF) { - str += (cp >> 12) + 224; - str += ((cp >> 6) & 63) + 128; - str += (cp & 63) + 128; + str += char((cp >> 12) + 224); + str += char(((cp >> 6) & 63) + 128); + str += char((cp & 63) + 128); } else if (cp <= 0x10FFFF) { - str += (cp >> 18) + 240; - str += ((cp >> 12) & 63) + 128; - str += ((cp >> 6) & 63) + 128; - str += (cp & 63) + 128; + str += char((cp >> 18) + 240); + str += char(((cp >> 12) & 63) + 128); + str += char(((cp >> 6) & 63) + 128); + str += char((cp & 63) + 128); } } diff --git a/lib/jsonParse.ll b/lib/jsonParse.ll index 0fc27e9..7e0b8ca 100644 --- a/lib/jsonParse.ll +++ b/lib/jsonParse.ll @@ -9,6 +9,8 @@ %{ #include <string> +#include <utility> +class jsonBaseFlexLexer; #include "jsonParse.h" #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" diff --git a/lib/persistance.cpp b/lib/persistance.cpp new file mode 100644 index 0000000..7539b44 --- /dev/null +++ b/lib/persistance.cpp @@ -0,0 +1,74 @@ +#include "persistance.h" +#include <map> + +namespace Persistanace { + using NamedTypeFactories = std::map<std::string_view, std::function<std::unique_ptr<Persistable>()>>; + static NamedTypeFactories namedTypeFactories; + + void + Persistable::addFactory(const std::string_view t, std::function<std::unique_ptr<Persistable>()> f) + { + namedTypeFactories.emplace(t, std::move(f)); + } + + std::unique_ptr<Persistable> + Persistable::callFactory(const std::string_view t) + { + return namedTypeFactories.at(t)(); + } + + void + Selection::operator()(float &) + { + throw std::runtime_error("Unexpected float"); + } + + void + Selection::operator()(bool &) + { + throw std::runtime_error("Unexpected bool"); + } + + void + Selection::operator()(const std::nullptr_t &) + { + throw std::runtime_error("Unexpected null"); + } + + void + Selection::operator()(std::string &) + { + throw std::runtime_error("Unexpected string"); + } + + void + Selection::BeginArray(Stack &) + { + throw std::runtime_error("Unexpected array"); + } + + SelectionPtr + Selection::BeginObject() + { + throw std::runtime_error("Unexpected object"); + } + + void + Selection::beforeValue(Stack &) + { + throw std::runtime_error("Unexpected value"); + } + + SelectionPtr + Selection::select(const std::string &) + { + throw std::runtime_error("Unexpected persist"); + } + + static_assert(!SelectionT<float>::ArrayLike); + static_assert(!SelectionT<bool>::ArrayLike); + static_assert(!SelectionT<std::string>::ArrayLike); + static_assert(SelectionT<std::vector<float>>::ArrayLike); + static_assert(SelectionT<glm::vec3>::ArrayLike); + static_assert(SelectionT<std::vector<glm::vec3>>::ArrayLike); +} diff --git a/lib/persistance.h b/lib/persistance.h new file mode 100644 index 0000000..75222e0 --- /dev/null +++ b/lib/persistance.h @@ -0,0 +1,201 @@ +#ifndef PERSISTANCE_H +#define PERSISTANCE_H + +#include <functional> +#include <glm/glm.hpp> +#include <iosfwd> +#include <memory> +#include <special_members.hpp> +#include <stack> +#include <stdexcept> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace glm { + template<glm::length_t L, typename T, glm::qualifier Q> struct vec; +} + +namespace Persistanace { + struct Selection; + using SelectionPtr = std::unique_ptr<Selection>; + using Stack = std::stack<SelectionPtr>; + + struct Selection { + Selection() = default; + virtual ~Selection() = default; + DEFAULT_MOVE_COPY(Selection); + + virtual void operator()(float &); + virtual void operator()(bool &); + virtual void operator()(const std::nullptr_t &); + virtual void operator()(std::string &); + virtual void BeginArray(Stack &); + virtual SelectionPtr BeginObject(); + virtual void beforeValue(Stack &); + virtual SelectionPtr select(const std::string &); + }; + + template<typename T> struct SelectionT : public Selection { + explicit SelectionT(T & value) : v {value} { } + + void + beforeValue(Stack &) override + { + } + + void + operator()(T & evalue) override + { + std::swap(v, evalue); + } + + static constexpr bool ArrayLike {false}; + T & v; + }; + + struct PersistanceStore { + // virtual bool persistType(const std::type_info &) = 0; + template<typename T> + bool + persistValue(const std::string_view & key, T & value) + { + if (key == name) { + sel = std::make_unique<SelectionT<T>>(std::ref(value)); + return false; + } + return true; + } + const std::string & name; + SelectionPtr sel {}; + }; + + template<glm::length_t L, typename T, glm::qualifier Q> struct SelectionT<glm::vec<L, T, Q>> : public Selection { + explicit SelectionT(glm::vec<L, float, Q> & value) : v {value} { } + + void + BeginArray(Stack &) override + { + } + + void + beforeValue(Stack & stk) override + { + stk.push(std::make_unique<SelectionT<T>>(std::ref(v[idx++]))); + } + + static constexpr bool ArrayLike {true}; + glm::vec<L, T, Q> & v; + glm::length_t idx {0}; + }; + + template<typename T> struct SelectionT<std::vector<T>> : public Selection { + explicit SelectionT(std::vector<T> & value) : v {value} { } + + void + BeginArray(Stack &) override + { + } + + void + beforeValue(Stack & stk) override + { + stk.push(std::make_unique<SelectionT<T>>(std::ref(v.emplace_back()))); + } + + static constexpr bool ArrayLike {true}; + std::vector<T> & v; + }; + + template<typename T> + concept ArrayLike = SelectionT<T>::ArrayLike; + + template<ArrayLike T> struct SelectionT<std::vector<T>> : public Selection { + explicit SelectionT(std::vector<T> & value) : v {value} { } + + void + BeginArray(Stack & stk) override + { + stk.push(std::make_unique<SelectionT<T>>(std::ref(v.emplace_back()))); + } + + static constexpr bool ArrayLike {true}; + std::vector<T> & v; + }; + + struct Persistable { + Persistable() = default; + virtual ~Persistable() = default; + DEFAULT_MOVE_COPY(Persistable); + + virtual void persist(PersistanceStore & store) = 0; + + static void addFactory(const std::string_view, std::function<std::unique_ptr<Persistable>()>); + static std::unique_ptr<Persistable> callFactory(const std::string_view); + }; + + template<typename T> struct SelectionT<std::unique_ptr<T>> : public Selection { + using Ptr = std::unique_ptr<T>; + struct SelectionObj : public Selection { + struct MakeObjectByTypeName : public Selection { + explicit MakeObjectByTypeName(Ptr & o) : o {o} { } + + void + beforeValue(Stack &) override + { + } + + void + operator()(std::string & type) override + { + auto no = Persistable::callFactory(type); + if (dynamic_cast<T *>(no.get())) { + o.reset(static_cast<T *>(no.release())); + } + } + + Ptr & o; + }; + + explicit SelectionObj(Ptr & o) : v {o} { } + + SelectionPtr + select(const std::string & mbr) override + { + using namespace std::literals; + if (mbr == "@typeid"sv) { + if (v) { + throw std::runtime_error("cannot set object type after creation"); + } + return std::make_unique<MakeObjectByTypeName>(std::ref(v)); + } + else { + if (!v) { + if constexpr (!std::is_abstract_v<T>) { + v = std::make_unique<T>(); + } + throw std::runtime_error("cannot select member of null object"); + } + PersistanceStore ps {mbr}; + v->persist(ps); + return std::move(ps.sel); + } + } + + Ptr & v; + }; + + explicit SelectionT(std::unique_ptr<T> & o) : v {o} { } + + SelectionPtr + BeginObject() override + { + return std::make_unique<SelectionObj>(v); + } + + std::unique_ptr<T> & v; + }; +} + +#endif diff --git a/test/test-persistance.cpp b/test/test-persistance.cpp index 7d66675..b6bde3f 100644 --- a/test/test-persistance.cpp +++ b/test/test-persistance.cpp @@ -2,237 +2,97 @@ #include <boost/test/unit_test.hpp> -#include <gfx/models/vertex.hpp> +#include <functional> #include <glm/glm.hpp> +#include <iosfwd> #include <jsonParse.h> -#include <maths.h> #include <memory> -#include <stack> -#include <stream_support.hpp> -#include <utility> -#include <variant> +#include <persistance.h> +#include <string> #include <vector> -struct PersistanceStore; -struct Selection; -using SelectionPtr = std::unique_ptr<Selection>; -struct Selection { - virtual ~Selection() = default; - virtual void - operator()(float &) - { - throw std::runtime_error("Unexpected float"); - } - virtual void - operator()(bool &) - { - throw std::runtime_error("Unexpected bool"); - } - virtual void - operator()(const std::nullptr_t &) - { - throw std::runtime_error("Unexpected null"); - } - virtual void - operator()(std::string &) - { - throw std::runtime_error("Unexpected string"); - } - virtual void - BeginArray() - { - throw std::runtime_error("Unexpected array"); - } - virtual SelectionPtr - BeginObject() - { - throw std::runtime_error("Unexpected object"); - } - virtual SelectionPtr select(std::string) - { - throw std::runtime_error("Unexpected persist"); - } -}; - -template<typename T> struct SelectionT : public Selection { - SelectionT(T & value) : v {value} { } - void - operator()(T & evalue) override - { - std::swap(v, evalue); - } - T & v; -}; - -template<glm::length_t L, glm::qualifier Q> struct SelectionT<glm::vec<L, float, Q>> : public Selection { - SelectionT(glm::vec3 & value) : v {value} - { - CLOG(__PRETTY_FUNCTION__); - } - void - operator()(float & value) override - { - v[idx++] = value; - } - void - BeginArray() override - { - } - glm::vec3 & v; - glm::vec3::length_type idx {0}; -}; - -template<typename T> struct SelectionT<std::vector<T>> : public Selection { - SelectionT(std::vector<T> & value) : v {value} { } - void - operator()(T & value) override - { - v.push_back(value); - } - void - BeginArray() - { - } - std::vector<T> & v; -}; - struct TestObject; -template<typename Ptr> struct MakeObjectByTypeName : public Selection { - MakeObjectByTypeName(Ptr & o) : o {o} { } - virtual void - operator()(std::string &) - { - o = std::make_unique<TestObject>(); // TODO shared_ptr - } - Ptr & o; -}; +namespace Persistanace { + struct JsonLoadPersistanceStore : public json::jsonParser { + explicit JsonLoadPersistanceStore(std::istream & in) : json::jsonParser {&in} { } -struct PersistanceStore { - // virtual bool persistType(const std::type_info &) = 0; - template<typename T> - bool - persistValue(const std::string_view & key, T & value) - { - if (key == name) { - sel = std::make_unique<SelectionT<T>>(std::ref(value)); - return false; - } - return true; - } - const std::string & name; - std::unique_ptr<Selection> sel {}; -}; + Stack stk; -template<typename Ptr> struct SelectionObj : public Selection { - SelectionObj(Ptr & o) : v {o} { } + inline SelectionPtr & + current() + { + return stk.top(); + } - SelectionPtr - select(std::string mbr) override - { - if (mbr == "@typeid") { - return std::make_unique<MakeObjectByTypeName<Ptr>>(std::ref(v)); + template<typename T> + void + loadState(std::unique_ptr<T> & t) + { + stk.push(std::make_unique<SelectionT<std::unique_ptr<T>>>(std::ref(t))); + yy_push_state(0); + yylex(); } - else if (v) { - CLOG(mbr); - PersistanceStore ps {mbr}; - v->persist(ps); - return std::move(ps.sel); + void + BeginObject() override + { + stk.push(current()->BeginObject()); } - throw std::runtime_error("cannot select member of null object"); - } - - Ptr & v; -}; - -template<typename T> struct SelectionT<std::unique_ptr<T>> : public Selection { - SelectionT(std::unique_ptr<T> & o) : v {o} { } - SelectionPtr - BeginObject() override - { - CLOG(__PRETTY_FUNCTION__); - return std::make_unique<SelectionObj<std::unique_ptr<T>>>(v); - } - std::unique_ptr<T> & v; -}; - -struct Persistable { - virtual void persist(PersistanceStore & store) = 0; -}; - -struct JsonLoadPersistanceStore : public json::jsonParser { - JsonLoadPersistanceStore(std::istream & in) : json::jsonParser {&in} { } - - std::stack<std::unique_ptr<Selection>> stk; - std::unique_ptr<Selection> selection; - - template<typename T> - void - loadState(std::unique_ptr<T> & t) - { - yy_push_state(0); - stk.push(std::make_unique<SelectionT<std::unique_ptr<T>>>(std::ref(t))); - yylex(); - } - void - BeginObject() override - { - CLOG(__FUNCTION__); - stk.push(stk.top()->BeginObject()); - } - void - BeginArray() override - { - CLOG(__FUNCTION__); - selection->BeginArray(); - } - void - PushBoolean(bool value) override - { - CLOG(__FUNCTION__); - (*selection)(value); - } - void - PushNumber(float value) override - { - CLOG(__FUNCTION__); - (*selection)(value); - } - void - PushNull() override - { - CLOG(__FUNCTION__); - (*selection)(nullptr); - } - void - PushText(std::string && value) override - { - CLOG(__FUNCTION__); - (*selection)(value); - } - void - PushKey(std::string && k) override - { - CLOG(__FUNCTION__); - selection = stk.top()->select(std::move(k)); - } - void - EndArray() override - { - CLOG(__FUNCTION__); - } - void - EndObject() override - { - CLOG(__FUNCTION__); - stk.pop(); - } -}; + void + BeginArray() override + { + current()->BeginArray(stk); + } + template<typename T> + inline void + PushValue(T && value) + { + current()->beforeValue(stk); + (*current())(value); + stk.pop(); + } + void + PushBoolean(bool value) override + { + PushValue(value); + } + void + PushNumber(float value) override + { + PushValue(value); + } + void + PushNull() override + { + PushValue(nullptr); + } + void + PushText(std::string && value) override + { + PushValue(value); + } + void + PushKey(std::string && k) override + { + stk.push(current()->select(k)); + } + void + EndArray() override + { + stk.pop(); + } + void + EndObject() override + { + stk.pop(); + } + }; +} #define STORE_TYPE store.persistType(typeid(*this)) -#define STORE_MEMBER(mbr) store.persistValue(#mbr##sv, mbr) +#define STORE_MEMBER(mbr) store.persistValue(#mbr, mbr) -struct TestObject : public Persistable { +struct TestObject : public Persistanace::Persistable { TestObject() = default; float flt {}; @@ -240,12 +100,15 @@ struct TestObject : public Persistable { bool bl {}; glm::vec3 pos {}; std::vector<float> flts; + std::vector<glm::vec3> poss; + std::vector<std::vector<std::vector<std::string>>> nest; + std::unique_ptr<TestObject> ptr; void - persist(PersistanceStore & store) override + persist(Persistanace::PersistanceStore & store) override { - using namespace std::literals; - STORE_MEMBER(flt) && STORE_MEMBER(str) && STORE_MEMBER(bl) && STORE_MEMBER(pos) && STORE_MEMBER(flts); + STORE_MEMBER(flt) && STORE_MEMBER(str) && STORE_MEMBER(bl) && STORE_MEMBER(pos) && STORE_MEMBER(flts) + && STORE_MEMBER(poss) && STORE_MEMBER(nest) && STORE_MEMBER(ptr); } }; @@ -256,12 +119,20 @@ const std::string input(R"J({ "bl": true, "pos": [3.14, 6.28, 1.57], "flts": [3.14, 6.28, 1.57, 0, -1, -3.14], + "poss": [[3.14, 6.28, 1.57], [0, -1, -3.14]], + "nest": [[["a","b"],["c","d","e"]],[["f"]],[]], + "ptr": { + "@typeid": "TestObject", + "flt": 3.14, + "str": "Lovely string", + }, })J"); BOOST_AUTO_TEST_CASE(load_object) { + Persistanace::Persistable::addFactory("TestObject", std::make_unique<TestObject>); std::stringstream ss {input}; - JsonLoadPersistanceStore jlps {ss}; + Persistanace::JsonLoadPersistanceStore jlps {ss}; std::unique_ptr<TestObject> to; jlps.loadState(to); @@ -278,4 +149,21 @@ BOOST_AUTO_TEST_CASE(load_object) BOOST_CHECK_CLOSE(to->flts[3], 0, 0.01); BOOST_CHECK_CLOSE(to->flts[4], -1, 0.01); BOOST_CHECK_CLOSE(to->flts[5], -3.14, 0.01); + BOOST_REQUIRE_EQUAL(to->poss.size(), 2); + BOOST_CHECK_CLOSE(to->poss[0][0], 3.14, 0.01); + BOOST_CHECK_CLOSE(to->poss[0][1], 6.28, 0.01); + BOOST_CHECK_CLOSE(to->poss[0][2], 1.57, 0.01); + BOOST_CHECK_CLOSE(to->poss[1][0], 0, 0.01); + BOOST_CHECK_CLOSE(to->poss[1][1], -1, 0.01); + BOOST_CHECK_CLOSE(to->poss[1][2], -3.14, 0.01); + BOOST_REQUIRE_EQUAL(to->nest.size(), 3); + BOOST_REQUIRE_EQUAL(to->nest.at(0).size(), 2); + BOOST_REQUIRE_EQUAL(to->nest.at(0).at(0).size(), 2); + BOOST_REQUIRE_EQUAL(to->nest.at(0).at(1).size(), 3); + BOOST_REQUIRE_EQUAL(to->nest.at(1).size(), 1); + BOOST_REQUIRE_EQUAL(to->nest.at(1).at(0).size(), 1); + BOOST_REQUIRE_EQUAL(to->nest.at(2).size(), 0); + BOOST_REQUIRE(to->ptr); + BOOST_CHECK_CLOSE(to->ptr->flt, 3.14, 0.01); + BOOST_CHECK_EQUAL(to->ptr->str, "Lovely string"); } |