diff --git a/include/ten/jserial.hh b/include/ten/jserial.hh index 4b617ad..c909a88 100644 --- a/include/ten/jserial.hh +++ b/include/ten/jserial.hh @@ -5,166 +5,266 @@ #include "ten/error.hh" #include +// FIXME: in theory I shouldn't have to do this, but SafeInt<> defines +// operator &. revisit on rename to archive(). +#include "ten/SafeInt3.hpp" + namespace ten { -using std::move; -// KV<> wraps a named field -// ConstKV<> wraps a named field that is not dynamic +// kvt<> wraps a named field +// const_kvt<> wraps a named field that is not dynamic -template struct KV { +template struct kvt { const char *key; V &&value; - KV(const char *k, V &&v) : key(k), value(v) {} + kvt(const char *k, V &&v) : key(k), value(v) {} }; -template struct ConstKV : public KV { - ConstKV(const char *k, V &&v) : KV(k, v) {} +template struct const_kvt : public kvt { + const_kvt(const char *k, V &&v) : kvt(k, v) {} }; -template KV kv(const char *k, V &&v) { return KV(k, v); } -template ConstKV const_kv(const char *k, V &&v) { return ConstKV(k, v); } +template inline kvt< V&> kv(const char *k, V &v) { return {k, v}; } +template inline kvt< const V&> kv(const char *k, const V &v) { return {k, v}; } +template inline const_kvt< V&> const_kv(const char *k, V &v) { return {k, v}; } +template inline const_kvt const_kv(const char *k, const V &v) { return {k, v}; } -// JSave +// json_archive, now with virtuality! -enum save_mode { - save_all, - save_dynamic +enum archive_mode { + archive_dynamic, + archive_all }; -class JSave { - private: +class json_saver; +class json_loader; + +class json_archive { + protected: json _j; unsigned _version; - save_mode _mode; + archive_mode _mode; + + json_archive(unsigned ver = 0, archive_mode mode = archive_all) : _version(ver), _mode(mode) {} + + json_archive(const json_archive &) = delete; + void operator = (const json_archive &) = delete; public: using is_archive = std::true_type; - using is_save = std::true_type; - using is_load = std::false_type; - - explicit JSave(unsigned ver = 0, save_mode mode = save_all) - : _version(ver), _mode(mode) {} - save_mode mode() const { return _mode; } - unsigned version() const { return _version; } + virtual bool is_save_v() const = 0; + bool is_load_v() const { return !is_save_v(); } - // make one like the other but without memory - JSave clone() const { return JSave(_version, _mode); } + unsigned version() const { return _version; } + archive_mode mode() const { return _mode; } - // public value extraction - friend json result(const JSave &ar) { return ar._j; } - friend json result( JSave &&ar) { return move(ar._j); } + bool empty() const { return empty_v(); } + virtual bool empty_v() const = 0; - // operator << works on save only, - template JSave & operator << (T &t) { return *this & t; } - template JSave & operator << ( KV f) { return *this & f; } - template JSave & operator << (ConstKV f) { return *this & f; } + virtual void vserialize(json &j) = 0; + virtual void vserialize(json &j, const char *key, archive_mode min_mode) = 0; - // save functions + template json_archive & operator & (T &t) { + bool is_save = is_save_v(); + json j; + if (is_save) { + j = result(json_saver(_version, _mode) & t); + if (!j) + return *this; + } + vserialize(j); + if (!is_save) + json_loader(j, _version, _mode) & t; + return *this; + } - JSave & operator & (const json &j) { - req_empty_for(j); - _j = j; + template json_archive & operator & (kvt f) { + bool is_save = is_save_v(); + json j; + if (is_save) { + j = result(json_saver(_version, _mode) & f.value); + if (!j) + return *this; + } + vserialize(j, f.key, archive_all); + if (!is_save && j) + json_loader(j, _version, _mode) & f.value; return *this; } - template ::can_make>::type> - JSave & operator & (const T &t) { - json j(to_json(t)); - req_empty_for(j); - _j = move(j); + template json_archive & operator & (const_kvt f) { + if (_mode >= archive_dynamic) + *this & static_cast &>(f); return *this; } +}; - // kv pair specialization - template - JSave & operator & (KV f) { - req_obj_for(f.key); - auto js = clone(); - auto jv = result(js & f.value); - if (jv) - _j.set(f.key, move(jv)); - return *this; +// json_saver + +class json_saver : public json_archive { + public: + explicit json_saver(unsigned ver = 0, archive_mode mode = archive_all) + : json_archive(ver, mode) {} + + using is_save = std::true_type; + using is_load = std::false_type; + + bool empty() const { return !_j; } + + // public value extraction + friend json result(const json_saver &ar) { return ar._j; } + friend json result( json_saver &&ar) { return move(ar._j); } + + // serialization, main interface + friend void serialize (json_saver &ar, const json &j) { + ar.req_empty_for(j); + ar._j = j; + } + template ::can_make>::type> + friend void serialize(json_saver &ar, const T &t) { + serialize(ar, json_traits::make(t)); } + // kvt<> and const_kvt<> specialization template - JSave & operator & (ConstKV kv) { - if (_mode == save_all) - *this & static_cast&>(kv); - return *this; + friend void serialize(json_saver &ar, kvt f) { + ar.req_obj_for(f.key); + json_saver ar2(ar._version, ar._mode); + serialize(ar2, f.value); + if (!ar2.empty()) + ar._j.set(f.key, result(ar2)); + } + template + friend void serialize(json_saver &ar, const_kvt f) { + if (ar._mode > archive_dynamic) + serialize(ar, static_cast &>(f)); + } + + // SafeInt<> specialization - because SafeInt<> has operator & + template + friend void serialize(json_saver &ar, const SafeInt &si) { + serialize(ar, *si.Ptr()); + } + + // runtime polymorphic interface - premade json only + bool is_save_v() const override { + return true; + } + bool empty_v() const override { + return empty(); + } + void vserialize(json &j) override { + serialize(*this, j); + } + void vserialize(json &j, const char *key, archive_mode min_mode) override { + if (_mode >= min_mode) + serialize(*this, kv(key, j)); } + // eye candy + template json_saver & operator & (T &t) { serialize(*this, t); return *this; } + template json_saver & operator & ( kvt f) { serialize(*this, f); return *this; } + template json_saver & operator & (const_kvt f) { serialize(*this, f); return *this; } + + template json_saver & operator << (T &t) { serialize(*this, t); return *this; } + template json_saver & operator << ( kvt f) { serialize(*this, f); return *this; } + template json_saver & operator << (const_kvt f) { serialize(*this, f); return *this; } + protected: // convenience for save functions void req_empty_for(const json &jn) { - if (_j) throw errorx("JSave multiple: %s & %s", _j.dump().c_str(), jn.dump().c_str()); + if (_j) throw errorx("json_saver multiple: %s & %s", _j.dump().c_str(), jn.dump().c_str()); } void req_obj_for(const char *key) { if (!_j) _j = json::object(); - else if (!_j.is_object()) throw errorx("JSave key '%s' to non-object: %s", key, _j.dump().c_str()); + else if (!_j.is_object()) throw errorx("json_saver key '%s' to non-object: %s", key, _j.dump().c_str()); } }; -// JLoad - -class JLoad { - json _j; - unsigned _version; +// json_loader +class json_loader : public json_archive { public: - using is_archive = std::true_type; + explicit json_loader(const json & j, unsigned ver = 0, archive_mode mode = archive_all) + : json_archive(ver, mode) { _j = j; } + explicit json_loader( json &&j, unsigned ver = 0, archive_mode mode = archive_all) + : json_archive(ver, mode) { _j = j; } + using is_save = std::false_type; using is_load = std::true_type; - JLoad(const json & j, unsigned ver = 0) : _j(j), _version(ver) {} - JLoad( json &&j, unsigned ver = 0) : _j(j), _version(ver) {} - - json source() const { return _j; } - unsigned version() const { return _version; } + bool empty() const { return !_j; } - // make one like the other but without memory - JLoad clone() const { return JLoad(_version); } - - // operator >> works on load only, - template JLoad & operator >> (T &t) { return *this & t; } - template JLoad & operator >> ( KV f) { return *this & f; } // not ref - template JLoad & operator >> (ConstKV f) { return *this & f; } // not ref - - // load functions - - JLoad & operator & (json &j) { - j = _j; - return *this; + // serialization, main interface + friend void serialize (json_loader &ar, json &j) { + j = ar._j; } - - template ::can_cast>::type> - JLoad & operator & (T &t) { - t = json_cast(_j); - return *this; + template ::can_cast>::type> + friend void serialize(json_loader &ar, T &t) { + t = json_traits::cast(ar._j); } + // kvt<> and const_kvt<> specialization template - JLoad & operator & (KV f) { - req_obj_for(f.key); - json jv(_j[f.key]); + friend void serialize(json_loader &ar, kvt f) { + ar.req_obj_for(f.key); + json jv(ar._j[f.key]); if (jv) { - JLoad js(move(jv), _version); - js & f.value; + json_loader ar2(std::move(jv), ar._version, ar._mode); + serialize(ar2, f.value); } - return *this; + } + template + friend void serialize(json_loader &ar, const_kvt f) { + if (ar._mode > archive_dynamic) + serialize(ar, static_cast &>(f)); + } + + // SafeInt<> specialization - because SafeInt<> has operator & + template + friend void serialize(json_loader &ar, SafeInt &si) { + serialize(ar, *si.Ptr()); + } + + // runtime polymorphic interface - premade json only + bool is_save_v() const override { + return false; + } + bool empty_v() const override { + return empty(); + } + void vserialize(json &j) override { + serialize(*this, j); + } + void vserialize(json &j, const char *key, archive_mode min_mode) override { + if (_mode >= min_mode) + serialize(*this, kv(key, j)); } + // eye candy + template json_loader & operator & (T &t) { serialize(*this, t); return *this; } + template json_loader & operator & ( kvt f) { serialize(*this, f); return *this; } + template json_loader & operator & (const_kvt f) { serialize(*this, f); return *this; } + + template json_loader & operator >> (T &t) { serialize(*this, t); return *this; } + template json_loader & operator >> ( kvt f) { serialize(*this, f); return *this; } + template json_loader & operator >> (const_kvt f) { serialize(*this, f); return *this; } + protected: // convenience for load functions void req_obj_for(const char *key) { - if (!_j.is_object()) throw errorx("JLoad key '%s' from non-object %s", key, _j.dump().c_str()); + if (!_j.is_object()) throw errorx("json_loader key '%s' from non-object %s", key, _j.dump().c_str()); } }; -template inline json jsave_all(T &t) { JSave ar(save_all); ar << t; return result(move(ar)); } -template inline json jsave_dyn(T &t) { JSave ar(save_dynamic); ar << t; return result(move(ar)); } + +// global functions + +template inline json jsave_all(const T &t) { json_saver ar(0, archive_all); ar << const_cast(t); return result(std::move(ar)); } +template inline json jsave_dyn(const T &t) { json_saver ar(0, archive_dynamic); ar << const_cast(t); return result(std::move(ar)); } } // ten diff --git a/include/ten/jserial_assoc.hh b/include/ten/jserial_assoc.hh index 49f81e1..137de78 100644 --- a/include/ten/jserial_assoc.hh +++ b/include/ten/jserial_assoc.hh @@ -2,7 +2,7 @@ #define LIBTEN_JSERIAL_ASSOC_HH #include -#if BOOST_VERSION > 104800 +#if BOOST_VERSION >= 104800 #include #endif #include @@ -34,35 +34,31 @@ struct json_traits_assoc : public json_traits { a.insert(value_type(jel.first, json_traits::cast(jel.second))); return a; } -}; -template -inline json to_json_assoc(const Assoc &a) { - json j{}; - for (auto const & el : a) - j.set(el.first, to_json(el.second)); - return j; -} + static json make(const Assoc &a) { + json j({}); + for (auto const & el : a) + j.set(el.first, json_traits::make(el.second)); + return j; + } +}; // // map<>, unordered_map<>, flat_map<> // template -struct json_traits> : public json_traits_assoc> {}; +struct json_traits> + : public json_traits_assoc> {}; template -struct json_traits> : public json_traits_assoc> {}; +struct json_traits> + : public json_traits_assoc> {}; #if BOOST_VERSION >= 104800 template -struct json_traits> : public json_traits_assoc> {}; -#endif - -template inline json to_json(const map &m) { return to_json_assoc(m); } -template inline json to_json(const unordered_map &m) { return to_json_assoc(m); } -#if BOOST_VERSION >= 104800 -template inline json to_json(const flat_map &m) { return to_json_assoc(m); } +struct json_traits> + : public json_traits_assoc> {}; #endif } // ten diff --git a/include/ten/jserial_enum.hh b/include/ten/jserial_enum.hh index 6b4e6e0..7a81de5 100644 --- a/include/ten/jserial_enum.hh +++ b/include/ten/jserial_enum.hh @@ -13,31 +13,38 @@ using std::string; namespace detail { template -inline AR & serialize_enum(AR &ar, E &e, Iter names_start, Iter names_end, std::true_type) { - return ar & json::str(*(names_start + (ptrdiff_t)e)); +inline void serialize_enum(AR &ar, E &e, Iter names_start, Iter /*names_end*/, std::true_type) { + json j = json::str(*(names_start + (ptrdiff_t)e)); + ar & j; } template -inline AR & serialize_enum(AR &ar, E &e, Iter names_start, Iter names_end, std::false_type) { +inline void serialize_enum(AR &ar, E &e, Iter names_start, Iter names_end, std::false_type) { json j; ar & j; if (j.is_string()) { auto nit = std::find(names_start, names_end, j.str()); if (nit != names_end) { e = E(nit - names_start); - return ar; + return; } } throw errorx("invalid %s: %s", typeid(E).name(), j.dump().c_str()); } } // detail -template -inline AR & serialize_enum(AR &ar, E &e, Iter names_start, Iter names_end) { - return detail::serialize_enum(ar, e, names_start, names_end, typename AR::is_save()); +// static +template +inline void serialize_enum(AR &ar, E &e, const Coll &c) { + detail::serialize_enum(ar, e, c.begin(), c.end(), IsSave()); } -template -inline AR & serialize_enum(AR &ar, E &e, const Coll &c) { - return detail::serialize_enum(ar, e, c.begin(), c.end(), typename AR::is_save()); + +// virtual +template +inline void serialize_enum(json_archive &ar, E &e, const Coll &c) { + if (ar.is_save_v()) + detail::serialize_enum(ar, e, c.begin(), c.end(), std::true_type()); + else + detail::serialize_enum(ar, e, c.begin(), c.end(), std::false_type()); } } // ten diff --git a/include/ten/jserial_maybe.hh b/include/ten/jserial_maybe.hh index 6c618bf..2b4a356 100644 --- a/include/ten/jserial_maybe.hh +++ b/include/ten/jserial_maybe.hh @@ -8,30 +8,39 @@ namespace ten { namespace detail { -using std::move; template inline void serialize(AR &ar, maybe &m, std::true_type) { if (m.ok()) - ar << m.get_ref(); + ar & m.get_ref(); } template inline void serialize(AR &ar, maybe &m, std::false_type) { - if (ar.source()) { + if (!ar.empty()) { T t; - ar >> t; - m = move(t); + ar & t; + m = std::move(t); } } } // detail -template -inline AR & operator & (AR &ar, maybe &m) { - detail::serialize(ar, m, typename AR::is_save()); - return ar; +// static +template +inline void serialize(AR &ar, maybe &m) { + detail::serialize(ar, m, IsSave()); +} + +// virtual +template +inline void serialize(json_archive &ar, maybe &m) { + if (ar.is_save_v()) + detail::serialize(ar, m, std::true_type()); + else + detail::serialize(ar, m, std::false_type()); } + } // ten #endif // LIBTEN_JSERIAL_MAYBE_HH diff --git a/include/ten/jserial_safeint.hh b/include/ten/jserial_safeint.hh index 01321a3..b65a256 100644 --- a/include/ten/jserial_safeint.hh +++ b/include/ten/jserial_safeint.hh @@ -10,17 +10,17 @@ using std::move; template struct json_traits> : public json_traits_conv> { - static SafeInt cast(const json &j) { return json_cast(j); } + static SafeInt cast(const json &j) { return json_traits::cast(j); } + static json make(SafeInt i) { return json_traits::make(i); } }; -template -inline json to_json(SafeInt i) { return to_json(i.Ref()); } - +#if 0 // This has moved to jserial due to SafeInt<> having an operator & template ::type> inline AR & operator & (AR &ar, SafeInt &si) { ar & *si.Ptr(); return ar; } +#endif } // ten diff --git a/include/ten/jserial_seq.hh b/include/ten/jserial_seq.hh index 56bc184..4d1f1fd 100644 --- a/include/ten/jserial_seq.hh +++ b/include/ten/jserial_seq.hh @@ -2,6 +2,7 @@ #define LIBTEN_JSERIAL_SEQ_HH #include +#include #include namespace ten { @@ -23,23 +24,26 @@ struct json_traits_seq : public json_traits { s.push_back(json_traits::cast(jel)); return s; } + + static json make(const Seq &s) { + json j(json::array()); + for (auto const & el : s) + j.push(json_traits::make(el)); + return j; + } }; -template -inline json to_json_seq(const Seq &s) { - json j(json::array()); - for (auto const & el : s) - j.push(to_json(el)); - return j; -} +// array<> -// vector<> +template +struct json_traits> + : public json_traits_seq> {}; -template -struct json_traits> : public json_traits_seq> {}; +// vector<> template -inline json to_json(const std::vector &v) { return to_json_seq(v); } +struct json_traits> + : public json_traits_seq> {}; } // ten diff --git a/include/ten/json.hh b/include/ten/json.hh index 210a464..dbd06f1 100644 --- a/include/ten/json.hh +++ b/include/ten/json.hh @@ -414,8 +414,8 @@ inline json to_json(bool b) { return json(b); } // conversions do not work for unspecified types template struct json_traits { typedef T type; - static const bool can_make = false; // to_json(T) works - static const bool can_cast = false; // trait::cast() works + static const bool can_make = false; // trait::make() -> json works + static const bool can_cast = false; // trait::cast() -> T works }; // convenience base class for conversions that work @@ -423,18 +423,29 @@ template struct json_traits_conv { typedef T type; static const bool can_make = true; static const bool can_cast = true; + + static json make(T t) { return json(t); } static T cast(const json &j); // default decl for most cases }; // json_cast<> function, a la lexical_cast<> -template ::can_cast>::type> -inline T json_cast(const json &j) { +template +inline typename std::enable_if::can_cast, T>::type +json_cast(const json &j) { return json_traits::cast(j); } +// make_json<> function, rather like to_json() but with a precise type +template +inline typename std::enable_if::can_cast, T>::type +make_json(T t) { + return json_traits::make(t); +} + // identity template <> struct json_traits : public json_traits_conv { static json cast(const json &j) { return j; } + static json make(const json &j) { return j; } }; // string @@ -443,6 +454,8 @@ template <> struct json_traits { typedef const char *type; static const bool can_make = true; // makes copy static const bool can_cast = false; // sorry, private pointer + + static json make(const char *s) { return json(s); } }; // integer diff --git a/include/ten/maybe.hh b/include/ten/maybe.hh index 656791f..4a6fdc5 100644 --- a/include/ten/maybe.hh +++ b/include/ten/maybe.hh @@ -6,7 +6,6 @@ #include namespace ten { -using std::move; //---------------------------------------------------------------- // maybe<> @@ -33,10 +32,10 @@ template class maybe { maybe(nothing_t) : _ok() {} maybe(const T &val) : _ok() { reset(val); } maybe( T &&val) : _ok() { reset(val); } - maybe(const maybe &m) : _ok() { if (m.ok()) reset( m.get_ref() ); } - maybe( maybe &&m) : _ok() { if (m.ok()) reset(move(m.get_ref())); } - maybe & operator = (const maybe &m) { if (m.ok()) reset( m.get_ref() ); else reset(); return *this; } - maybe & operator = (const maybe &&m) { if (m.ok()) reset(move(m.get_ref())); else reset(); return *this; } + maybe(const maybe &m) : _ok() { if (m.ok()) reset( m.get_ref() ); } + maybe( maybe &&m) : _ok() { if (m.ok()) reset(std::move(m.get_ref())); } + maybe & operator = (const maybe &m) { if (m.ok()) reset( m.get_ref() ); else reset(); return *this; } + maybe & operator = ( maybe &&m) { if (m.ok()) reset(std::move(m.get_ref())); else reset(); return *this; } maybe & operator = (nothing_t) { reset(); return *this; } ~maybe() { reset(); } diff --git a/tests/test_json.cc b/tests/test_json.cc index b6f81f6..79ebe76 100644 --- a/tests/test_json.cc +++ b/tests/test_json.cc @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(json_test_filter_key_exists) { } BOOST_AUTO_TEST_CASE(json_test_truth) { - json o(json::object()); + json o({}); // empty init list BOOST_CHECK(o.get("nothing").is_true() == false); BOOST_CHECK(o.get("nothing").is_false() == false); BOOST_CHECK(o.get("nothing").is_null() == false); @@ -202,9 +202,9 @@ BOOST_AUTO_TEST_CASE(json_conversions) { } BOOST_AUTO_TEST_CASE(json_create) { - json obj1; - obj1.set("test", "set"); + json obj1({}); BOOST_CHECK(obj1); + obj1.set("test", "set"); BOOST_CHECK(obj1.get("test")); json root{ {"obj1", obj1} @@ -218,6 +218,7 @@ BOOST_AUTO_TEST_CASE(json_create) { } #ifdef TEN_JSON_CXX11 + struct corge { int foo, bar; @@ -225,8 +226,8 @@ struct corge { corge(int foo_, int bar_) : foo(foo_), bar(bar_) {} }; template -inline AR & operator & (AR &ar, corge &c) { - return ar & kv("foo", c.foo) & kv("bar", c.bar); +inline void serialize(AR &ar, corge &c) { + ar & kv("foo", c.foo) & kv("bar", c.bar); } inline bool operator == (const corge &a, const corge &b) { return a.foo == b.foo && a.bar == b.bar; @@ -236,8 +237,8 @@ inline bool operator == (const corge &a, const corge &b) { enum captain { kirk, picard, janeway, sisko }; const array captain_names = {{ "kirk", "picard", "janeway", "sisko" }}; template -inline AR & operator & (AR &ar, captain &c) { - return serialize_enum(ar, c, captain_names); +inline void serialize(AR &ar, captain &c) { + serialize_enum(ar, c, captain_names); } @@ -245,11 +246,11 @@ BOOST_AUTO_TEST_CASE(json_serial) { corge c1(42, 17); auto j = jsave_all(c1); corge c2; - JLoad(j) >> c2; + json_loader(j) >> c2; BOOST_CHECK(c1 == c2); map m; - JLoad(j) >> m; + json_loader(j) >> m; BOOST_CHECK_EQUAL(m.size(), 2); BOOST_CHECK(m.find("foo") != m.end()); BOOST_CHECK(m.find("bar") != m.end()); @@ -258,7 +259,7 @@ BOOST_AUTO_TEST_CASE(json_serial) { #if BOOST_VERSION >= 104800 flat_map f; - JLoad(j) >> f; + json_loader(j) >> f; BOOST_CHECK_EQUAL(f.size(), 2); BOOST_CHECK(f.find("foo") != f.end()); BOOST_CHECK(f.find("bar") != f.end()); @@ -276,16 +277,15 @@ BOOST_AUTO_TEST_CASE(json_serial) { a.reset(); BOOST_CHECK(!a.ok()); j = 17; - JLoad(j) >> a; + json_loader(j) >> a; BOOST_CHECK(a.ok()); BOOST_CHECK_EQUAL(a.get(), 17); - captain c = janeway; j = jsave_all(c); BOOST_CHECK_EQUAL(j, "janeway"); j = "kirk"; - JLoad(j) >> c; + json_loader(j) >> c; BOOST_CHECK_EQUAL(c, kirk); }