Commit e088cfb1 authored by gbalduzz's avatar gbalduzz
Browse files

Avoid exception upon type conversion failure and cleaned up conversion routines.

parent 80df272f
Loading
Loading
Loading
Loading
+142 −139
Original line number Diff line number Diff line
@@ -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
+4 −2
Original line number Diff line number Diff line
@@ -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.
+1 −7
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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);
+14 −1
Original line number Diff line number Diff line
@@ -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());
}
@@ -94,5 +108,4 @@ std::string findLine(std::istream& inp, const std::streampos& pos) {
  return std::to_string(line);
}


}  // namespace dca::io::details