File Serialize.hpp
File List > core > Serialize.hpp
Go to the documentation of this file
#pragma once
#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>
namespace cse491 {
// --- Pre-declarations of Functions ---
template <typename T> static void SerializeValue_Vector(std::ostream &, const std::vector<T> &);
template <typename T> static void SerializeValue_Map(std::ostream &, const T &);
template <typename T> static void DeserializeValue_Vector(std::istream &, std::vector<T> &);
template <typename T> static void DeserializeValue_Map(std::istream &, T &);
// --- Type Traits ---
template <typename T> struct is_vector : std::false_type {};
template <typename T> struct is_vector<std::vector<T>> : std::true_type {};
template <typename T>
struct is_any_map : std::false_type {};
template <typename KEY_T, typename VALUE_T>
struct is_any_map<std::map<KEY_T, VALUE_T>> : std::true_type {};
template <typename KEY_T, typename VALUE_T>
struct is_any_map<std::unordered_map<KEY_T, VALUE_T>> : std::true_type {};
template <typename STREAM_T, typename OBJ_T>
concept CanStreamTo = requires(STREAM_T & stream, OBJ_T value) {
{ stream << value } -> std::convertible_to<std::ostream&>;
};
template <typename STREAM_T, typename OBJ_T>
concept CanStreamFrom = requires(STREAM_T & stream, OBJ_T value) {
{ stream >> value } -> std::convertible_to<std::istream&>;
};
template <typename OBJ_T>
concept HasSerialize = requires(OBJ_T value) {
{ value.Serialize(std::cout) } -> std::same_as<void>;
};
template <typename OBJ_T>
concept HasDeserialize = requires(OBJ_T value) {
{ value.Deserialize(std::cin) } -> std::same_as<void>;
};
template <typename T>
static void SerializeValue(std::ostream & os, const T & var) {
if constexpr (std::is_enum<T>()) {
os << static_cast<int>(var) << std::endl;
} else if constexpr (is_vector<T>()) {
SerializeValue_Vector(os, var);
} else if constexpr (is_any_map<T>()) {
SerializeValue_Map(os, var);
} else if constexpr (HasSerialize<T>) {
var.Serialize(os);
} else if constexpr (CanStreamTo<std::stringstream, T>) {
os << var << '\n';
} else {
}
}
template <typename T>
static void SerializeValue_Vector(std::ostream & os, const std::vector<T> & var) {
SerializeValue(os, var.size());
for (const auto & x : var) {
SerializeValue(os, x);
}
}
template <typename T>
static void SerializeValue_Map(std::ostream & os, const T & var) {
SerializeValue(os, var.size());
for (const auto & [key, value] : var) {
SerializeValue(os, key);
SerializeValue(os, value);
}
}
template <typename T>
static void DeserializeValue(std::istream & is, T & var) {
static_assert(!std::is_const<T>(), "Cannot deserialize const variables.");
// If we are loading a string, load it directly.
if constexpr (std::is_same<std::decay_t<T>, std::string>()) {
std::getline(is, var, '\n');
} else if constexpr (is_vector<T>()) {
DeserializeValue_Vector(is, var);
} else if constexpr (is_any_map<T>()) {
DeserializeValue_Map(is, var);
} else if constexpr (HasDeserialize<T>) {
var.Deserialize(is);
} else {
// @CAO: This can be streamlined to use only the original is, and based on type.
// For example, "is << var" followed by "is.peek()" to make sure we have a
// newline, and then "is.ignore()" to skip the newline.
std::string str;
std::getline(is, str, '\n');
std::stringstream ss(str);
if constexpr (std::is_enum<T>()) { // enums must be converted properly.
int enum_val;
ss >> enum_val;
var = static_cast<T>(enum_val);
} else if constexpr (CanStreamFrom<std::stringstream, T>) {
ss >> var;
} else if constexpr (std::is_pointer<T>()) {
std::cerr << "Warning: Attempting to deserialize pointer." << std::endl;
} else {
// Finally, ignore this value? Most likely a pointer.
std::cerr << "Warning: Attempting to deserialize unknown type." << std::endl;
}
}
}
template <typename T>
static void DeserializeFunction(std::istream & is, std::function<void(T)> set_fun) {
std::string str;
std::getline(is, str, '\n');
if constexpr (std::is_same<std::decay_t<T>, std::string>()) {
set_fun(str);
} else if constexpr (std::is_same<std::decay_t<T>, int>()) {
set_fun(stoi(str));
} else if constexpr (std::is_same<std::decay_t<T>, double>()) {
set_fun(stod(str));
} else {
T var;
std::stringstream ss(str);
ss >> var;
set_fun(var);
}
}
template <typename T>
static T DeserializeAs(std::istream & is) {
T value;
DeserializeValue(is, value);
return value;
}
template <typename T>
static void DeserializeValue_Vector(std::istream & is, std::vector<T> & var) {
DeserializeFunction<size_t>(is, [&var](size_t in_size){ var.resize(in_size); } );
for (auto & x : var) {
DeserializeValue(is, x);
}
}
template <typename MAP_T>
static void DeserializeValue_Map(std::istream & is, MAP_T & var) {
size_t map_size = 0;
typename MAP_T::key_type key;
typename MAP_T::mapped_type value;
DeserializeValue(is, map_size);
for (size_t i = 0; i < map_size; ++i) {
DeserializeValue(is, key);
DeserializeValue(is, value);
var[key] = value;
}
}
} // End of namespace cse491