From 7a11d761e7608e807cef0b913915f15ffae499e9 Mon Sep 17 00:00:00 2001
From: Vasileios Karakasis <karakasis@cscs.ch>
Date: Wed, 3 Feb 2016 16:18:39 +0100
Subject: [PATCH] Refactoring: Implementation details in separate files.

Moved SWC parser's and cell record's implementation details to a
separate file. Also added braces around single-line control/loop blocks.
---
 CMakeLists.txt       |   6 +-
 src/CMakeLists.txt   |   8 ++
 src/swcio.cpp        | 214 +++++++++++++++++++++++++++++++++++++++++++
 src/swcio.hpp        | 181 ++----------------------------------
 tests/CMakeLists.txt |  11 +--
 tests/test_swcio.cpp |   7 +-
 6 files changed, 241 insertions(+), 186 deletions(-)
 create mode 100644 src/CMakeLists.txt
 create mode 100644 src/swcio.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 717eb124..ea981fe4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,11 +11,11 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -Wall")
 set(CMAKE_EXPORT_COMPILE_COMMANDS "YES")
 
 # generated .a and .so go into /lib
-#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
-#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 
 include_directories(${CMAKE_SOURCE_DIR}/src)
 include_directories(${CMAKE_SOURCE_DIR})
 
+add_subdirectory(src)
 add_subdirectory(tests)
-
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..90c9d8f8
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(HEADERS
+    swcio.hpp
+    )
+set(BASE_SOURCES
+    swcio.cpp
+)
+
+add_library(cellalgo ${BASE_SOURCES} ${HEADERS})
diff --git a/src/swcio.cpp b/src/swcio.cpp
new file mode 100644
index 00000000..129f70af
--- /dev/null
+++ b/src/swcio.cpp
@@ -0,0 +1,214 @@
+#include <iomanip>
+#include <map>
+#include <sstream>
+#include <unordered_set>
+#include <swcio.hpp>
+
+namespace nestmc
+{
+namespace io
+{
+
+//
+// cell_record implementation
+// 
+void cell_record::renumber(id_type new_id, std::map<id_type, id_type> &idmap)
+{
+    auto old_id = id_;
+    id_ = new_id;
+
+    // Obtain parent_id from the map
+    auto new_parent_id = idmap.find(parent_id_);
+    if (new_parent_id != idmap.end()) {
+        parent_id_ = new_parent_id->second;
+    }
+
+    check_consistency();
+    idmap.insert(std::make_pair(old_id, new_id));
+}
+
+void cell_record::check_consistency() const
+{
+    // Check cell type as well; enum's do not offer complete type safety,
+    // since you can cast anything that fits to its underlying type
+    if (type_ < 0 || type_ > custom) {
+        throw std::invalid_argument("unknown cell type");
+    }
+
+    if (id_ < 0) {
+        throw std::invalid_argument("negative ids not allowed");
+    }
+        
+    if (parent_id_ < -1) {
+        throw std::invalid_argument("parent_id < -1 not allowed");
+    }
+
+    if (parent_id_ >= id_) {
+        throw std::invalid_argument("parent_id >= id is not allowed");
+    }
+
+    if (r_ < 0) {
+        throw std::invalid_argument("negative radii are not allowed");
+    }
+}
+
+std::istream &operator>>(std::istream &is, cell_record &cell)
+{
+    swc_parser parser;
+    parser.parse_record(is, cell);
+    return is;
+}
+
+
+std::ostream &operator<<(std::ostream &os, const cell_record &cell)
+{
+    // output in one-based indexing
+    os << cell.id_+1 << " "
+       << cell.type_ << " "
+       << std::setprecision(7) << cell.x_ << " "
+       << std::setprecision(7) << cell.y_ << " "
+       << std::setprecision(7) << cell.z_ << " "
+       << std::setprecision(7) << cell.r_ << " "
+       << ((cell.parent_id_ == -1) ? cell.parent_id_ : cell.parent_id_+1);
+
+    return os;
+}
+
+
+//
+// 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)
+{
+    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");
+}
+
+template<typename T>
+T parse_value_strict(std::istream &is)
+{
+    T val;
+    check_parse_status(is >> val);
+
+    // everything's fine
+    return val;
+}
+
+// specialize parsing for cell types
+template<>
+cell_record::kind parse_value_strict(std::istream &is)
+{
+    cell_record::id_type val;
+    check_parse_status(is >> val);
+
+    // Let cell_record's constructor check for the type validity
+    return static_cast<cell_record::kind>(val);
+}
+
+//
+// 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_);
+        if (!linebuff_.empty() && !starts_with(linebuff_, comment_prefix_))
+            break;
+    }
+
+    if (is.bad()) {
+        // let the caller check for such events
+        return is;
+    }
+
+    if (is.eof() &&
+        (linebuff_.empty() || starts_with(linebuff_, comment_prefix_))) {
+        // last line is either empty or a comment; don't parse anything
+        return is;
+    }
+
+    if (is.fail()) {
+        throw swc_parse_error("too long line detected");
+    }
+
+    std::istringstream line(linebuff_);
+    cell = parse_record(line);
+    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);
+
+    // Convert to zero-based, leaving parent_id as-is if -1
+    if (parent_id != -1) {
+        parent_id--;
+    }
+
+    return cell_record(type, id-1, x, y, z, r, parent_id);
+}
+
+
+std::vector<cell_record> swc_read_cells(std::istream &is)
+{
+    std::vector<cell_record> cells;
+    std::unordered_set<cell_record::id_type> ids;
+
+    std::size_t          num_trees = 0;
+    cell_record::id_type last_id   = -1;
+    bool                 needsort  = false;
+
+    cell_record curr_cell;
+    while ( !(is >> curr_cell).eof()) {
+        if (curr_cell.parent() == -1 && ++num_trees > 1) {
+            // only a single tree is allowed
+            break;
+        }
+
+        auto inserted = ids.insert(curr_cell.id());
+        if (inserted.second) {
+            // not a duplicate; insert cell
+            cells.push_back(curr_cell);
+            if (!needsort && curr_cell.id() < last_id) {
+                needsort = true;
+            }
+
+            last_id = curr_cell.id();
+        }
+    }
+
+    if (needsort) {
+        std::sort(cells.begin(), cells.end());
+    }
+
+    // Renumber cells if necessary
+    std::map<cell_record::id_type, cell_record::id_type> idmap;
+    cell_record::id_type next_id = 0;
+    for (auto &c : cells) {
+        if (c.id() != next_id) {
+            c.renumber(next_id, idmap);
+        }
+
+        ++next_id;
+    }
+
+    return std::move(cells);
+}
+
+}   // end of nestmc::io
+}   // end of nestmc
diff --git a/src/swcio.hpp b/src/swcio.hpp
index 63043904..ba52720a 100644
--- a/src/swcio.hpp
+++ b/src/swcio.hpp
@@ -2,10 +2,7 @@
 
 #include <exception>
 #include <iostream>
-#include <map>
-#include <sstream>
-#include <type_traits>
-#include <unordered_set>
+#include <string>
 #include <vector>
 
 namespace nestmc
@@ -15,11 +12,6 @@ namespace io
 {
 
 
-static bool starts_with(const std::string &str, const std::string &prefix)
-{
-    return (str.find(prefix) == 0);
-}
-
 class cell_record 
 {
 public:
@@ -147,41 +139,10 @@ public:
         return 2*r_;
     }
 
-    void renumber(id_type new_id, std::map<id_type, id_type> &idmap)
-    {
-        auto old_id = id_;
-        id_ = new_id;
-
-        // Obtain parent_id from the map
-        auto new_parent_id = idmap.find(parent_id_);
-        if (new_parent_id != idmap.end()) {
-            parent_id_ = new_parent_id->second;
-        }
-
-        check_consistency();
-        idmap.insert(std::make_pair(old_id, new_id));
-    }
+    void renumber(id_type new_id, std::map<id_type, id_type> &idmap);
 
 private:
-    void check_consistency() const
-    {
-        // Check cell type as well; enum's do not offer complete type safety,
-        // since you can cast anything that fits to its underlying type
-        if (type_ < 0 || type_ > custom)
-            throw std::invalid_argument("unknown cell type");
-
-        if (id_ < 0)
-            throw std::invalid_argument("negative ids not allowed");
-        
-        if (parent_id_ < -1)
-            throw std::invalid_argument("parent_id < -1 not allowed");
-
-        if (parent_id_ >= id_)
-            throw std::invalid_argument("parent_id >= id is not allowed");
-
-        if (r_ < 0)
-            throw std::invalid_argument("negative radii are not allowed");
-    }
+    void check_consistency() const;
 
     kind type_;         // cell type
     id_type id_;        // cell id
@@ -216,50 +177,9 @@ public:
         , comment_prefix_("#")
     { }
 
-    std::istream &parse_record(std::istream &is, cell_record &cell)
-    {
-        while (!is.eof() && !is.bad()) {
-            // consume empty and comment lines first
-            std::getline(is, linebuff_);
-            if (!linebuff_.empty() && !starts_with(linebuff_, comment_prefix_))
-                break;
-        }
-
-        if (is.bad())
-            // let the caller check for such events
-            return is;
-
-        if (is.eof() &&
-            (linebuff_.empty() || starts_with(linebuff_, comment_prefix_)))
-            // last line is either empty or a comment; don't parse anything
-            return is;
-
-        if (is.fail())
-            throw swc_parse_error("too long line detected");
-
-        std::istringstream line(linebuff_);
-        cell = parse_record(line);
-        return is;
-    }
+    std::istream &parse_record(std::istream &is, cell_record &cell);
 
 private:
-    void check_parse_status(const std::istream &is)
-    {
-        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");
-    }
-
-    template<typename T>
-    T parse_value_strict(std::istream &is)
-    {
-        T val;
-        check_parse_status(is >> val);
-
-        // everything's fine
-        return val;
-    }
-
     // Read the record from a string stream; will be treated like a single line
     cell_record parse_record(std::istringstream &is);
 
@@ -269,56 +189,7 @@ private:
 };
 
 
-// specialize parsing for cell types
-template<>
-cell_record::kind swc_parser::parse_value_strict(std::istream &is)
-{
-    cell_record::id_type val;
-    check_parse_status(is >> val);
-
-    // Let cell_record's constructor check for the type validity
-    return static_cast<cell_record::kind>(val);
-}
-
-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<int>(is);
-
-    // Convert to zero-based, leaving parent_id as-is if -1
-    if (parent_id != -1)
-        parent_id--;
-
-    return cell_record(type, id-1, x, y, z, r, parent_id);
-}
-
-
-std::istream &operator>>(std::istream &is, cell_record &cell)
-{
-    swc_parser parser;
-    parser.parse_record(is, cell);
-    return is;
-}
-
-
-std::ostream &operator<<(std::ostream &os, const cell_record &cell)
-{
-    // output in one-based indexing
-    os << cell.id_+1 << " "
-       << cell.type_ << " "
-       << std::setprecision(7) << cell.x_ << " "
-       << std::setprecision(7) << cell.y_ << " "
-       << std::setprecision(7) << cell.z_ << " "
-       << std::setprecision(7) << cell.r_ << " "
-       << ((cell.parent_id_ == -1) ? cell.parent_id_ : cell.parent_id_+1);
-
-    return os;
-}
+std::istream &operator>>(std::istream &is, cell_record &cell);
 
 //
 // Reads cells from an input stream until an eof is encountered and returns a
@@ -327,47 +198,7 @@ std::ostream &operator<<(std::ostream &os, const cell_record &cell)
 // For more information check here:
 //   https://github.com/eth-cscs/cell_algorithms/wiki/SWC-file-parsing
 //
-std::vector<cell_record> swc_read_cells(std::istream &is)
-{
-    std::vector<cell_record> cells;
-    std::unordered_set<cell_record::id_type> ids;
-
-    std::size_t          num_trees = 0;
-    cell_record::id_type last_id   = -1;
-    bool                 needsort  = false;
-
-    cell_record curr_cell;
-    while ( !(is >> curr_cell).eof()) {
-        if (curr_cell.parent() == -1 && ++num_trees > 1)
-            // only a single tree is allowed
-            break;
-
-        auto inserted = ids.insert(curr_cell.id());
-        if (inserted.second) {
-            // not a duplicate; insert cell
-            cells.push_back(curr_cell);
-            if (!needsort && curr_cell.id() < last_id)
-                needsort = true;
-
-            last_id = curr_cell.id();
-        }
-    }
-
-    if (needsort)
-        std::sort(cells.begin(), cells.end());
-
-    // Renumber cells if necessary
-    std::map<cell_record::id_type, cell_record::id_type> idmap;
-    cell_record::id_type next_id = 0;
-    for (auto &c : cells) {
-        if (c.id() != next_id)
-            c.renumber(next_id, idmap);
-
-        ++next_id;
-    }
-
-    return std::move(cells);
-}
+std::vector<cell_record> swc_read_cells(std::istream &is);
 
 }   // end of nestmc::io
 }   // end of nestmc
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index ba8b481a..7e787ba4 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -18,10 +18,9 @@ set(TEST_SOURCES
 
 add_executable(test.exe ${TEST_SOURCES} ${HEADERS})
 
-#target_link_libraries(test_compiler LINK_PUBLIC compiler gtest)
-
-#set_target_properties( test.exe
-#   PROPERTIES
-#   RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
-#)
+target_link_libraries(test.exe LINK_PUBLIC cellalgo)
 
+set_target_properties(test.exe
+   PROPERTIES
+   RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests"
+)
diff --git a/tests/test_swcio.cpp b/tests/test_swcio.cpp
index ba62c90b..a3a7336a 100644
--- a/tests/test_swcio.cpp
+++ b/tests/test_swcio.cpp
@@ -117,14 +117,16 @@ TEST(swc_parser, invalid_input)
 
     {
         // Check non-parsable values
-        std::istringstream is("1a 1 14.566132 34.873772 7.857000 0.717830 -1\n");
+        std::istringstream is(
+            "1a 1 14.566132 34.873772 7.857000 0.717830 -1\n");
         cell_record cell;
         EXPECT_THROW(is >> cell, swc_parse_error);
     }
 
     {
         // Check invalid cell type
-        std::istringstream is("1 10 14.566132 34.873772 7.857000 0.717830 -1\n");
+        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);
     }
@@ -214,6 +216,7 @@ TEST(swc_parser, from_allen_db)
     while( !(fid >> node).eof()) {
         nodes.push_back(std::move(node));
     }
+
     // verify that the correct number of nodes was read
     EXPECT_EQ(nodes.size(), 1058u);
 }
-- 
GitLab