diff --git a/src/swcio.cpp b/src/swcio.cpp index e87eaa1846458f9d00d9b97e7c8c3de4bfc54a2a..32724fe8f38685a593c6d82cdbf27448c26a325a 100644 --- a/src/swcio.cpp +++ b/src/swcio.cpp @@ -13,7 +13,7 @@ namespace io // // cell_record implementation -// +// void cell_record::renumber(id_type new_id, std::map<id_type, id_type> &idmap) { auto old_id = id_; @@ -79,26 +79,26 @@ std::ostream &operator<<(std::ostream &os, const cell_record &cell) // // Utility functions -// +// bool starts_with(const std::string &str, const std::string &prefix) { return (str.find(prefix) == 0); } -void check_parse_status(const std::istream &is) +void check_parse_status(const std::istream &is, const swc_parser &parser) { if (is.fail()) { // If we try to read past the eof; fail bit will also be set - throw swc_parse_error("could not parse value"); + throw swc_parse_error("could not parse value", parser.lineno()); } } template<typename T> -T parse_value_strict(std::istream &is) +T parse_value_strict(std::istream &is, const swc_parser &parser) { T val; - check_parse_status(is >> val); + check_parse_status(is >> val, parser); // everything's fine return val; @@ -106,10 +106,10 @@ T parse_value_strict(std::istream &is) // specialize parsing for cell types template<> -cell_record::kind parse_value_strict(std::istream &is) +cell_record::kind parse_value_strict(std::istream &is, const swc_parser &parser) { cell_record::id_type val; - check_parse_status(is >> val); + check_parse_status(is >> val, parser); // Let cell_record's constructor check for the type validity return static_cast<cell_record::kind>(val); @@ -117,13 +117,14 @@ cell_record::kind parse_value_strict(std::istream &is) // // swc_parser implementation -// +// std::istream &swc_parser::parse_record(std::istream &is, cell_record &cell) { while (!is.eof() && !is.bad()) { // consume empty and comment lines first std::getline(is, linebuff_); + ++lineno_; if (!linebuff_.empty() && !starts_with(linebuff_, comment_prefix_)) break; } @@ -140,23 +141,29 @@ std::istream &swc_parser::parse_record(std::istream &is, cell_record &cell) } if (is.fail()) { - throw swc_parse_error("too long line detected"); + throw swc_parse_error("too long line detected", lineno_); } std::istringstream line(linebuff_); - cell = parse_record(line); + try { + cell = parse_record(line); + } catch (std::invalid_argument &e) { + // Rethrow as a parse error + throw swc_parse_error(e.what(), lineno_); + } + return is; } cell_record swc_parser::parse_record(std::istringstream &is) { - auto id = parse_value_strict<int>(is); - auto type = parse_value_strict<cell_record::kind>(is); - auto x = parse_value_strict<float>(is); - auto y = parse_value_strict<float>(is); - auto z = parse_value_strict<float>(is); - auto r = parse_value_strict<float>(is); - auto parent_id = parse_value_strict<cell_record::id_type>(is); + auto id = parse_value_strict<int>(is, *this); + auto type = parse_value_strict<cell_record::kind>(is, *this); + auto x = parse_value_strict<float>(is, *this); + auto y = parse_value_strict<float>(is, *this); + auto z = parse_value_strict<float>(is, *this); + auto r = parse_value_strict<float>(is, *this); + auto parent_id = parse_value_strict<cell_record::id_type>(is, *this); // Convert to zero-based, leaving parent_id as-is if -1 if (parent_id != -1) { diff --git a/src/swcio.hpp b/src/swcio.hpp index 0348eb4f149303362c736d885afae8d1d77c2eb5..5365c8a5b508f6102e06c08d780cd94a64aeae0b 100644 --- a/src/swcio.hpp +++ b/src/swcio.hpp @@ -166,13 +166,23 @@ private: class swc_parse_error : public std::runtime_error { public: - explicit swc_parse_error(const char *msg) + explicit swc_parse_error(const char *msg, std::size_t lineno) : std::runtime_error(msg) + , lineno_(lineno) { } - explicit swc_parse_error(const std::string &msg) + explicit swc_parse_error(const std::string &msg, std::size_t lineno) : std::runtime_error(msg) + , lineno_(lineno) { } + + std::size_t lineno() const + { + return lineno_; + } + +private: + std::size_t lineno_; }; class swc_parser @@ -182,13 +192,20 @@ public: std::string comment_prefix) : delim_(delim) , comment_prefix_(comment_prefix) + , lineno_(0) { } swc_parser() : delim_(" ") , comment_prefix_("#") + , lineno_(0) { } + std::size_t lineno() const + { + return lineno_; + } + std::istream &parse_record(std::istream &is, cell_record &cell); private: @@ -198,6 +215,7 @@ private: std::string delim_; std::string comment_prefix_; std::string linebuff_; + std::size_t lineno_; }; diff --git a/tests/test_swcio.cpp b/tests/test_swcio.cpp index 5912a1e5086bfe6a25120bf2dcfe72ccd6e9f1ba..16e6de8c4eb0d7a9b7c7de6264ba66a4f7f7a192 100644 --- a/tests/test_swcio.cpp +++ b/tests/test_swcio.cpp @@ -129,7 +129,7 @@ TEST(swc_parser, invalid_input) std::istringstream is( "1 10 14.566132 34.873772 7.857000 0.717830 -1\n"); cell_record cell; - EXPECT_THROW(is >> cell, std::invalid_argument); + EXPECT_THROW(is >> cell, swc_parse_error); } } @@ -340,12 +340,12 @@ TEST(cell_record_ranges, raw) } { + // Check iterator increments std::stringstream is; is << "1 1 14.566132 34.873772 7.857000 0.717830 -1\n"; - // Check postfix operator++ auto iter = get_cell_records<swc_io_raw>(is).begin(); - auto iend = get_cell_records<swc_io_raw>(is).end(); + auto iend = get_cell_records<swc_io_raw>(is).end(); cell_record c; EXPECT_NO_THROW(c = *iter++); @@ -355,4 +355,24 @@ TEST(cell_record_ranges, raw) // Try to read past eof EXPECT_THROW(*iter, std::out_of_range); } + + { + // Check parse error context + std::stringstream is; + is << "1 1 14.566132 34.873772 7.857000 0.717830 -1\n"; + is << "2 1 14.566132 34.873772 7.857000 0.717830 1\n"; + is << "3 10 14.566132 34.873772 7.857000 0.717830 1\n"; + is << "4 1 14.566132 34.873772 7.857000 0.717830 1\n"; + + std::vector<cell_record> cells; + try { + for (auto c : get_cell_records<swc_io_raw>(is)) { + cells.push_back(c); + } + + FAIL() << "expected an exception"; + } catch (const swc_parse_error &e) { + EXPECT_EQ(3u, e.lineno()); + } + } }