#ifndef CTTI_SERIALIZATION_HPP #define CTTI_SERIALIZATION_HPP #include #include #include #include #include #include #include namespace ctti { namespace serialization { template void serialize(Formatter formatter, Output& output, const T& value); template void serialize(Writer writer, const T& value); template void deserialize(Reader reader, T& value); namespace detail { template(), bool EmptyModel = ctti::meta::list_size>() == 0> struct serialize { using Model = ctti::get_model; template struct loop_body { Writer* writer; const T* value; template void operator()(SymbolIdentity, Index) { using Symbol = ctti::meta::type_t; constexpr std::size_t index = Index(); writer->value(Symbol::symbol().str(), ctti::get_member_value(*value)); if(index + 1 < ctti::meta::list_size()) { writer->value_separator(); } } }; template static void apply(Writer& writer, const T& value) { loop_body loop_body{&writer, &value}; writer.begin_object(value); ctti::meta::foreach(loop_body); writer.end_object(value); } }; template struct serialize { using Model = ctti::get_model; template struct loop_body { Writer* writer; const T* value; template void operator()(SymbolIdentity, Index) { using Symbol = ctti::meta::type_t; if(Symbol::template get_member() == *value) { // Found the enumeration value with this value writer->raw_value(Symbol::symbol().str()); } } }; template static void apply(Writer& writer, const T& value) { loop_body loop_body{&writer, &value}; ctti::meta::foreach(loop_body); } }; template struct serialize { template static void apply(Writer& writer, const T& value) { writer.value(value); } }; template::value, bool HasModel = ctti::has_model::value> struct deserialize { using Model = ctti::get_model; template struct loop_body { Reader* reader; T* value; template void operator()(SymbolIdentity, Index) { using Symbol = ctti::meta::type_t; reader->value(Symbol::symbol().str(), ctti::get_member_value(*value)); } }; template static void apply(Reader& reader, T& value) { loop_body loop_body{&reader, &value}; ctti::meta::foreach(loop_body); } }; template struct deserialize { using Model = ctti::get_model; template struct loop_body { T* value; InputValue input_value; template void operator()(SymbolIdentity, Index) { using Symbol = ctti::meta::type_t; if(static_cast::type>(Symbol::template get_member()) == static_cast::type>(input_value)) { *value = Symbol::template get_member(); } } }; template struct loop_body { T* value; std::string input_value; template void operator()(SymbolIdentity, Index) { using Symbol = ctti::meta::type_t; if(Symbol::symbol().str() == input_value) { *value = Symbol::template get_member(); } } }; struct callback { template T operator()(const InputValue& input_value) { T output_value{}; ctti::meta::foreach(loop_body{&output_value, input_value}); return output_value; } }; template static void apply(Reader& reader, T& value) { reader.enum_value(value, callback()); } }; template struct deserialize { template static void apply(Reader& reader, T& value) { reader.value(value); } }; } template struct Writer { Writer(Formatter formatter, Output& output) : _formatter{formatter}, _output{&output} {} void value_separator() { _formatter.value_separator(*_output); } template void raw_value(const T& value) { _formatter.raw_value(*_output, value); } template void value(const T& value) { _formatter.value(*_output, value); } template void value(const std::string& name, const T& value) { _formatter.value(*_output, name, value); } template void begin_object(const T& object) { _formatter.begin_object(*_output, object); } template void end_object(const T& object) { _formatter.end_object(*_output, object); } private: Formatter _formatter; Output* _output; }; template Writer make_writer(Formatter formatter, Output& output) { return {formatter, output}; } struct ostream_otuput { ostream_otuput(std::ostream& os) : os(&os) {} template void write(const T& value) { (*os) << value; } private: std::ostream* os; }; struct json_formatter { template void raw_value(Output& output, const T& value) { output.write(value); } template void value_separator(Output& out) { out.write(", "); } template void begin_object(Output& out, const T&) { out.write("{"); } template void end_object(Output& out, const T&) { out.write("}"); } template void value(Output& out, const std::string& name, const T& value) { out.write("\""); out.write(name); out.write("\": "); this->value(out, value); } template void value(Output& out, const T& value) { write_value(out, value, ctti::meta::bool_>() == 0>()); } template void value(Output& out, const std::string& value) { out.write("\""); out.write(value); out.write("\""); } template void value(Output& out, const std::vector& vector) { out.write("["); for(std::size_t i = 0; i < vector.size(); ++i) { ctti::serialization::serialize(*this, out, vector[i]); if(i + 1 < vector.size()) { out.write(", "); } } out.write("]"); } template void value(Output& out, const std::array& array) { out.write("["); for(std::size_t i = 0; i < array.size(); ++i) { ctti::serialization::serialize(*this, out, array[i]); if(i + 1 < array.size()) { out.write(", "); } } out.write("]"); } template void value(Output& out, const std::unordered_map& map) { out.write("["); std::size_t i = 0; for(const auto& keyValue : map) { out.write("{"); ctti::serialization::serialize(*this, out, keyValue.first); out.write(": "); ctti::serialization::serialize(*this, out, keyValue.second); out.write("}"); if(i + 1 < map.size()) { out.write(", "); } ++i; } out.write("]"); } template void value(Output& out, const std::tuple& tuple) { out.write("("); ctti::meta::foreach>(tuple_loop_body_t{&tuple, this, &out}); out.write(")"); } private: template struct tuple_loop_body_t { const std::tuple* tuple; json_formatter* self; Output* out; template void operator()(ctti::meta::identity, ctti::meta::size_t) const { ctti::serialization::serialize(*self, *out, std::get(*tuple)); if(Index + 1 < sizeof...(Ts)) { out->write(", "); } } }; template void write_value(Output& out, const T& value, ctti::meta::true_) { out.write(value); } template void write_value(Output& out, const T& value, ctti::meta::false_) { ctti::serialization::serialize(*this, out, value); } }; struct json_writer { json_writer(nlohmann::json& current_object) : _current_object{¤t_object} {} template ctti::meta::enable_if_t::value> value(const std::string& name, const T& value) { ctti::serialization::serialize(json_writer((*_current_object)[name]), value); } template ctti::meta::enable_if_t::value> value(const std::string& name, const T& value) { (*_current_object)[name] = nlohmann::json(value); } template void raw_value(const T& value) { *_current_object = nlohmann::json(value); } template ctti::meta::enable_if_t::value> value(const T& value) { ctti::serialization::serialize(json_writer(*_current_object), value); } template ctti::meta::enable_if_t::value> value(const T& value) { raw_value(value); } template void begin_object(const T&) { } template void end_object(const T&) { } void value_separator() {} private: nlohmann::json* _current_object; }; struct json_reader { json_reader(const nlohmann::json& json) : _current_object{&json} {} template ctti::meta::enable_if_t::value> value(T& value) { ctti::serialization::deserialize(json_reader(*_current_object), value); } template ctti::meta::enable_if_t::value> value(T& value) { value = _current_object->get(); } template void enum_value(T& value, Function callback) { if(_current_object->is_number()) { value = callback(_current_object->get::type>()); } else if(_current_object->is_string()) { value = callback(_current_object->get()); } } template ctti::meta::enable_if_t::value> value(const std::string& name, T& value) { ctti::serialization::deserialize(json_reader((*_current_object)[name]), value); } template ctti::meta::enable_if_t::value> value(const std::string& name, T& value) { auto subobject = (*_current_object)[name]; value = subobject.get(); } template void value(const std::string& name, std::unordered_map& map) { value((*_current_object)[name], map); } template void value(std::unordered_map& map) { value(*_current_object, map); } private: const nlohmann::json* _current_object; template void value(const nlohmann::json& json, std::unordered_map& map) { assert(json.is_array()); for(const auto& key_value_object : json) { // maps are serialized as key, value arrays, where key value pairs are arrays too assert(key_value_object.is_array()); assert(key_value_object.size() == 2); const auto& key = key_value_object[0]; const auto& value = key_value_object[1]; Key deserialized_key; Value deserialized_value; ctti::serialization::deserialize(json_reader(key), deserialized_key); ctti::serialization::deserialize(json_reader(value), deserialized_value); map[std::move(deserialized_key)] = std::move(deserialized_value); } } }; struct enum_from_string_reader { enum_from_string_reader(const std::string& input) : _input{input} {} template void enum_value(T& value, Callback callback) { value = callback(_input); } private: std::string _input; }; struct enum_to_string_writer { enum_to_string_writer(std::string& output) : _output{&output} {} void raw_value(const std::string& enum_value) { *_output = enum_value; } private: std::string* _output; }; template ctti::meta::enable_if_t::value && std::is_enum::value, std::string> enum_to_string(const Enum value) { std::string output; ctti::serialization::serialize(ctti::serialization::enum_to_string_writer(output), value); return output; } template ctti::meta::enable_if_t::value && std::is_enum::value, Enum> enum_from_string(const std::string& enum_string) { Enum enum_value{}; ctti::serialization::deserialize(ctti::serialization::enum_from_string_reader(enum_string), enum_value); return enum_value; } template void serialize(Writer writer, const T& value) { ctti::serialization::detail::serialize::apply(writer, value); } template void serialize(Formatter formatter, Output& output, const T& value) { auto writer = ctti::serialization::make_writer(formatter, output); ctti::serialization::serialize(writer, value); } template void serialize(Formatter formatter, Output output, const T& value) { auto writer = ctti::serialization::make_writer(formatter, output); ctti::serialization::serialize(writer, value); } template void deserialize(Reader reader, T& value) { ctti::serialization::detail::deserialize::apply(reader, value); } namespace detail { template>() == 0, typename = void> struct is_default_ostream_serializable : public ctti::meta::false_ {}; template struct is_default_ostream_serializable> : public ctti::meta::true_ {}; template struct is_default_ostream_serializable().ctti_ostream_print())>> : public ctti::meta::true_ {}; template struct is_default_ostream_serializable>()))>> : public ctti::meta::true_ {}; } } } template ctti::meta::enable_if_t::value, std::ostream&> operator<<(std::ostream& os, const T& value) { ctti::serialization::serialize(ctti::serialization::json_formatter(), ctti::serialization::ostream_otuput(os), value); return os; } #endif // CTTI_SERIALIZATION_HPP