Loading include/dca/io/json/details/convert.hpp +142 −139 Original line number Diff line number Diff line Loading @@ -17,200 +17,203 @@ #include <sstream> #include <vector> namespace dca::io { #include "dca/io/json/details/util.hpp" template <class T> std::istream& operator>>(std::istream& stream, std::vector<T>& vec); namespace dca::io { template <class T> struct Convert { static T execute(const std::string& val) { T ret; std::stringstream stream(val); stream >> ret; return ret; static bool execute(const std::string& inp, T& val) { std::stringstream stream(inp); try { stream >> val; } catch (...) { return false; } return true; } }; template <> struct Convert<std::string> { static std::string execute(const std::string& val) { if (val.size() > 1 && (val[0] == '\"' && val.back() == '\"')) return val.substr(1, val.size() - 2); else throw(std::logic_error("Not a string")); static bool execute(std::string_view inp, std::string& val) { inp = details::trimSpaces(inp); if (inp.size() >= 2 && inp[0] == '\"' && inp.back() == '\"') { val = inp.substr(1, inp.size() - 2); return true; } else { return false; } } }; template <> struct Convert<bool> { static bool execute(const std::string& val) { if (val.size() >= 4 && val.substr(0, 4) == "true") return true; else if (val.size() >= 5 && val.substr(0, 5) == "false") return false; static bool execute(std::string_view inp, bool& val) { inp = details::trimSpaces(inp); if (inp == "true") val = true; else if (inp == "false") val = false; else return std::stoi(val); val = std::atoi(inp.data()); return true; } }; template <class T1, class T2> struct Convert<std::pair<T1, T2>> { static auto execute(const std::string& val) { static bool execute(const std::string& inp, std::pair<T1, T2>& p) { static_assert(std::is_scalar_v<T1> && std::is_scalar_v<T2>, "composite pair members are not supported"); const auto split = val.find(','); if ((val.at(0) != '(' && val.back() != ')') || split == std::string::npos) throw(std::logic_error("Not a pair")); std::pair<T1, T2> p; p.first = Convert<T1>::execute(val.substr(1, split - 1)); p.second = Convert<T2>::execute(val.substr(split + 1, val.size() - split - 2)); return p; const auto split = inp.find(','); if ((inp.at(0) != '(' && inp.back() != ')') || split == std::string::npos) { // Not a pair return false; } }; template <class T1, class T2> std::ostream& operator<<(std::ostream& stream, const std::pair<T1, T2>& p) { return stream << '(' << p.first << ", " << p.second << ')'; } std::pair<T1, T2> result; bool success = true; template <class T> std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vec) { stream << "["; for (std::size_t i = 0; i < vec.size(); ++i) { if constexpr (std::is_same_v<T, std::string>) { stream << "\"" + vec[i] + "\""; success &= Convert<T1>::execute(inp.substr(1, split - 1), result.first); success &= Convert<T2>::execute(inp.substr(split + 1, inp.size() - split - 2), result.second); if (success) { p = std::move(result); return true; } else { stream << vec[i]; } if (i < vec.size() - 1) stream << ", "; } stream << "]"; return stream; return false; } template <class T, std::size_t n> std::ostream& operator<<(std::ostream& stream, const std::array<T, n>& arr) { return stream << std::vector<T>(arr.begin(), arr.end()); } }; template <class T, std::size_t n> std::istream& operator>>(std::istream& stream, std::array<T, n>& arr) { struct Convert<std::array<T, n>> { static bool execute(const std::string& inp, std::array<T, n>& arr) { std::vector<T> v; v.reserve(n); stream >> v; std::copy(v.begin(), v.end(), arr.begin()); return stream; } template <class T, std::size_t n> std::istream& operator>>(std::istream& stream, std::vector<std::array<T, n>>& v) { std::vector<std::vector<T>> vv; // Expansive but easy: delegate to vector conversion. const bool success = Convert<std::vector<T>>::execute(inp, v); if (!success || v.size() != n) return false; stream >> vv; v.resize(vv.size()); for (std::size_t i = 0; i < vv.size(); ++i) { std::copy(vv[i].begin(), vv[i].end(), v[i].begin()); } return stream; std::copy(v.begin(), v.end(), arr.begin()); return true; } }; template <class T> std::istream& operator>>(std::istream& stream, std::vector<T>& vec) { vec.clear(); struct Convert<std::vector<T>> { static bool execute(std::string_view inp, std::vector<T>& vec) { inp = details::trimSpaces(inp); char c; stream.read(&c, 1); if (c != '[') { stream.seekg(-1, stream.cur); throw(std::logic_error("invalid vector format")); if (inp.size() < 2 || inp[0] != '[' || inp.back() != ']') { // Invalid vector format. return false; } std::string value; std::vector<T> result; // move result into vec after it has been successfully read. bool quote = false; int parentheses = 0; std::size_t pos = 1; std::string entry; auto add_element = [&]() { bool success = true; if (entry != "") { result.emplace_back(); success = Convert<T>::execute(entry, result.back()); entry.clear(); } return success; }; while (pos < inp.size() - 1) { // skip first and last square bracket. const char c = inp[pos++]; while (stream.read(&c, 1)) { if (!quote) { switch (c) { case ']': if (value != "") vec.push_back(Convert<T>::execute(value)); return stream; case '[': case '(': ++parentheses; value.push_back(c); entry.push_back(c); break; case ')': case ']': --parentheses; value.push_back(c); if (parentheses < 0) { // imbalanced parentheses. return false; } entry.push_back(c); break; case ',': if (parentheses == 0) { vec.push_back(Convert<T>::execute(value)); value.clear(); if (!add_element()) return false; } else { value.push_back(c); entry.push_back(c); } break; case '\"': quote = true; value.push_back(c); break; case ' ': entry.push_back(c); break; default: value.push_back(c); entry.push_back(c); } } else { if (c == '\"') quote = false; value.push_back(c); entry.push_back(c); } } return stream; } template <class T> std::istream& operator>>(std::istream& stream, std::vector<std::vector<T>>& vec) { vec.clear(); // Process last element. if (!add_element()) return false; char c; stream.read(&c, 1); if (c != '[') { stream.seekg(-1, stream.cur); throw(std::logic_error("invalid vector format")); vec = std::move(result); return true; } }; while (true) { vec.emplace_back(); stream >> vec.back(); // trim whitespaces. while (stream.peek() == ' ') stream.read(&c, 1); stream.read(&c, 1); if (c == ']') break; assert(c == ','); while (stream.peek() == ' ') stream.read(&c, 1); template <class T1, class T2> std::ostream& operator<<(std::ostream& stream, const std::pair<T1, T2>& p) { return stream << '(' << p.first << ", " << p.second << ')'; } template <class T> std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vec) { stream << "["; for (std::size_t i = 0; i < vec.size(); ++i) { if constexpr (std::is_same_v<T, std::string>) { stream << "\"" + vec[i] + "\""; } else { stream << vec[i]; } if (i < vec.size() - 1) stream << ", "; } stream << "]"; return stream; } template <class T, std::size_t n> std::ostream& operator<<(std::ostream& stream, const std::array<T, n>& arr) { return stream << std::vector<T>(arr.begin(), arr.end()); } } // namespace dca::io #endif // DCA_IO_JSON_DETAILS_CONVERT_HPP include/dca/io/json/details/json_entry.hpp +4 −2 Original line number Diff line number Diff line Loading @@ -47,9 +47,11 @@ public: stream << data_; } // Returns the success of the write operation. A common failure is a type not compatible with the // string representation. In case of failure "obj: is unmodified. template <class T> void write(T& obj) const { obj = Convert<T>::execute(data_); bool write(T& obj) const { return Convert<T>::execute(data_, obj); } // Return true if this is the last element of a group. Loading include/dca/io/json/details/json_group.hpp +1 −7 Original line number Diff line number Diff line Loading @@ -70,13 +70,7 @@ bool JSONGroup::readEntry(const std::string& name, T& val) const noexcept { if (!entry) return false; try { entry->write(val); return true; } catch (...) { return false; } return entry->write(val); } } // namespace dca::io::details Loading include/dca/io/json/details/util.hpp +1 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ void skipUntil(std::istream& inp, char target); void trimUntil(std::istream& inp, char target); void trimSpaces(std::istream& inp); std::string_view trimSpaces(std::string_view s); std::string findLine(std::istream& inp); std::string findLine(std::istream& inp, const std::streampos& pos); Loading src/io/json/details/util.cpp +14 −1 Original line number Diff line number Diff line Loading @@ -74,6 +74,20 @@ void trimSpaces(std::istream& inp) { } } std::string_view trimSpaces(std::string_view s) { auto is_space = [](char c) { return c == ' ' || c == '\n' || c == '\t'; }; std::size_t start = 0; while (start < s.size() && is_space(s[start])) ++start; std::size_t end = s.size(); // exclusive while (end > 0 && is_space(s[end - 1])) --end; return s.substr(start, end - start); } std::string findLine(std::istream& inp) { return findLine(inp, inp.tellg()); } Loading @@ -94,5 +108,4 @@ std::string findLine(std::istream& inp, const std::streampos& pos) { return std::to_string(line); } } // namespace dca::io::details Loading
include/dca/io/json/details/convert.hpp +142 −139 Original line number Diff line number Diff line Loading @@ -17,200 +17,203 @@ #include <sstream> #include <vector> namespace dca::io { #include "dca/io/json/details/util.hpp" template <class T> std::istream& operator>>(std::istream& stream, std::vector<T>& vec); namespace dca::io { template <class T> struct Convert { static T execute(const std::string& val) { T ret; std::stringstream stream(val); stream >> ret; return ret; static bool execute(const std::string& inp, T& val) { std::stringstream stream(inp); try { stream >> val; } catch (...) { return false; } return true; } }; template <> struct Convert<std::string> { static std::string execute(const std::string& val) { if (val.size() > 1 && (val[0] == '\"' && val.back() == '\"')) return val.substr(1, val.size() - 2); else throw(std::logic_error("Not a string")); static bool execute(std::string_view inp, std::string& val) { inp = details::trimSpaces(inp); if (inp.size() >= 2 && inp[0] == '\"' && inp.back() == '\"') { val = inp.substr(1, inp.size() - 2); return true; } else { return false; } } }; template <> struct Convert<bool> { static bool execute(const std::string& val) { if (val.size() >= 4 && val.substr(0, 4) == "true") return true; else if (val.size() >= 5 && val.substr(0, 5) == "false") return false; static bool execute(std::string_view inp, bool& val) { inp = details::trimSpaces(inp); if (inp == "true") val = true; else if (inp == "false") val = false; else return std::stoi(val); val = std::atoi(inp.data()); return true; } }; template <class T1, class T2> struct Convert<std::pair<T1, T2>> { static auto execute(const std::string& val) { static bool execute(const std::string& inp, std::pair<T1, T2>& p) { static_assert(std::is_scalar_v<T1> && std::is_scalar_v<T2>, "composite pair members are not supported"); const auto split = val.find(','); if ((val.at(0) != '(' && val.back() != ')') || split == std::string::npos) throw(std::logic_error("Not a pair")); std::pair<T1, T2> p; p.first = Convert<T1>::execute(val.substr(1, split - 1)); p.second = Convert<T2>::execute(val.substr(split + 1, val.size() - split - 2)); return p; const auto split = inp.find(','); if ((inp.at(0) != '(' && inp.back() != ')') || split == std::string::npos) { // Not a pair return false; } }; template <class T1, class T2> std::ostream& operator<<(std::ostream& stream, const std::pair<T1, T2>& p) { return stream << '(' << p.first << ", " << p.second << ')'; } std::pair<T1, T2> result; bool success = true; template <class T> std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vec) { stream << "["; for (std::size_t i = 0; i < vec.size(); ++i) { if constexpr (std::is_same_v<T, std::string>) { stream << "\"" + vec[i] + "\""; success &= Convert<T1>::execute(inp.substr(1, split - 1), result.first); success &= Convert<T2>::execute(inp.substr(split + 1, inp.size() - split - 2), result.second); if (success) { p = std::move(result); return true; } else { stream << vec[i]; } if (i < vec.size() - 1) stream << ", "; } stream << "]"; return stream; return false; } template <class T, std::size_t n> std::ostream& operator<<(std::ostream& stream, const std::array<T, n>& arr) { return stream << std::vector<T>(arr.begin(), arr.end()); } }; template <class T, std::size_t n> std::istream& operator>>(std::istream& stream, std::array<T, n>& arr) { struct Convert<std::array<T, n>> { static bool execute(const std::string& inp, std::array<T, n>& arr) { std::vector<T> v; v.reserve(n); stream >> v; std::copy(v.begin(), v.end(), arr.begin()); return stream; } template <class T, std::size_t n> std::istream& operator>>(std::istream& stream, std::vector<std::array<T, n>>& v) { std::vector<std::vector<T>> vv; // Expansive but easy: delegate to vector conversion. const bool success = Convert<std::vector<T>>::execute(inp, v); if (!success || v.size() != n) return false; stream >> vv; v.resize(vv.size()); for (std::size_t i = 0; i < vv.size(); ++i) { std::copy(vv[i].begin(), vv[i].end(), v[i].begin()); } return stream; std::copy(v.begin(), v.end(), arr.begin()); return true; } }; template <class T> std::istream& operator>>(std::istream& stream, std::vector<T>& vec) { vec.clear(); struct Convert<std::vector<T>> { static bool execute(std::string_view inp, std::vector<T>& vec) { inp = details::trimSpaces(inp); char c; stream.read(&c, 1); if (c != '[') { stream.seekg(-1, stream.cur); throw(std::logic_error("invalid vector format")); if (inp.size() < 2 || inp[0] != '[' || inp.back() != ']') { // Invalid vector format. return false; } std::string value; std::vector<T> result; // move result into vec after it has been successfully read. bool quote = false; int parentheses = 0; std::size_t pos = 1; std::string entry; auto add_element = [&]() { bool success = true; if (entry != "") { result.emplace_back(); success = Convert<T>::execute(entry, result.back()); entry.clear(); } return success; }; while (pos < inp.size() - 1) { // skip first and last square bracket. const char c = inp[pos++]; while (stream.read(&c, 1)) { if (!quote) { switch (c) { case ']': if (value != "") vec.push_back(Convert<T>::execute(value)); return stream; case '[': case '(': ++parentheses; value.push_back(c); entry.push_back(c); break; case ')': case ']': --parentheses; value.push_back(c); if (parentheses < 0) { // imbalanced parentheses. return false; } entry.push_back(c); break; case ',': if (parentheses == 0) { vec.push_back(Convert<T>::execute(value)); value.clear(); if (!add_element()) return false; } else { value.push_back(c); entry.push_back(c); } break; case '\"': quote = true; value.push_back(c); break; case ' ': entry.push_back(c); break; default: value.push_back(c); entry.push_back(c); } } else { if (c == '\"') quote = false; value.push_back(c); entry.push_back(c); } } return stream; } template <class T> std::istream& operator>>(std::istream& stream, std::vector<std::vector<T>>& vec) { vec.clear(); // Process last element. if (!add_element()) return false; char c; stream.read(&c, 1); if (c != '[') { stream.seekg(-1, stream.cur); throw(std::logic_error("invalid vector format")); vec = std::move(result); return true; } }; while (true) { vec.emplace_back(); stream >> vec.back(); // trim whitespaces. while (stream.peek() == ' ') stream.read(&c, 1); stream.read(&c, 1); if (c == ']') break; assert(c == ','); while (stream.peek() == ' ') stream.read(&c, 1); template <class T1, class T2> std::ostream& operator<<(std::ostream& stream, const std::pair<T1, T2>& p) { return stream << '(' << p.first << ", " << p.second << ')'; } template <class T> std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vec) { stream << "["; for (std::size_t i = 0; i < vec.size(); ++i) { if constexpr (std::is_same_v<T, std::string>) { stream << "\"" + vec[i] + "\""; } else { stream << vec[i]; } if (i < vec.size() - 1) stream << ", "; } stream << "]"; return stream; } template <class T, std::size_t n> std::ostream& operator<<(std::ostream& stream, const std::array<T, n>& arr) { return stream << std::vector<T>(arr.begin(), arr.end()); } } // namespace dca::io #endif // DCA_IO_JSON_DETAILS_CONVERT_HPP
include/dca/io/json/details/json_entry.hpp +4 −2 Original line number Diff line number Diff line Loading @@ -47,9 +47,11 @@ public: stream << data_; } // Returns the success of the write operation. A common failure is a type not compatible with the // string representation. In case of failure "obj: is unmodified. template <class T> void write(T& obj) const { obj = Convert<T>::execute(data_); bool write(T& obj) const { return Convert<T>::execute(data_, obj); } // Return true if this is the last element of a group. Loading
include/dca/io/json/details/json_group.hpp +1 −7 Original line number Diff line number Diff line Loading @@ -70,13 +70,7 @@ bool JSONGroup::readEntry(const std::string& name, T& val) const noexcept { if (!entry) return false; try { entry->write(val); return true; } catch (...) { return false; } return entry->write(val); } } // namespace dca::io::details Loading
include/dca/io/json/details/util.hpp +1 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ void skipUntil(std::istream& inp, char target); void trimUntil(std::istream& inp, char target); void trimSpaces(std::istream& inp); std::string_view trimSpaces(std::string_view s); std::string findLine(std::istream& inp); std::string findLine(std::istream& inp, const std::streampos& pos); Loading
src/io/json/details/util.cpp +14 −1 Original line number Diff line number Diff line Loading @@ -74,6 +74,20 @@ void trimSpaces(std::istream& inp) { } } std::string_view trimSpaces(std::string_view s) { auto is_space = [](char c) { return c == ' ' || c == '\n' || c == '\t'; }; std::size_t start = 0; while (start < s.size() && is_space(s[start])) ++start; std::size_t end = s.size(); // exclusive while (end > 0 && is_space(s[end - 1])) --end; return s.substr(start, end - start); } std::string findLine(std::istream& inp) { return findLine(inp, inp.tellg()); } Loading @@ -94,5 +108,4 @@ std::string findLine(std::istream& inp, const std::streampos& pos) { return std::to_string(line); } } // namespace dca::io::details