0

Edit: I just noticed that within my CSV file, the iterator does work only when there is a newline as the last line in the file. Does this have to do with istream and not my implementation of this iterator?


I have the following class and iterator: For some reason, when using a for loop to iterate through, the loop exits immediately and doesn't execute any code within the loop. However, when I call get_row() manually, it works as intended.

template<typename ...ColType>
class CSV
{
public:
    using value_type = std::tuple<ColType...>;

    class iterator;

    explicit CSV(std::istream &in, const char delimiter = ',') : in_(in), delimiter(delimiter)
    {
    }

    std::tuple<ColType...> get_row()
    {
        auto next = std::stringstream(next_valid_line());
        std::tuple<ColType...> tuple;

        parse_row<>(next, tuple);
        return tuple;
    }

    [[nodiscard]] inline bool good() const { return in_.good(); }

    inline iterator begin() { return iterator(*this); }

    inline iterator end() { return iterator(); }

private:
    std::istream &in_;
    const char delimiter;

    [[nodiscard]] std::string next_valid_line() const
    {
        std::string line;
        while (std::getline(in_, line))
        {
            if (!line.empty() && !isspace(line.front()) && line.rfind(COMMENT_DELIMITER, 0) != 0)
            {
                return line;
            }
        }

        return "";
    }

    template<std::size_t Index = 0>
    void parse_row(std::stringstream &s, std::tuple<ColType...> &tuple)
    {
        if constexpr (sizeof...(ColType) > Index)
        {
            std::string col;
            std::getline(s, col, delimiter);
            trim(col);

            std::stringstream tmp(col);
            tmp >> std::get<Index>(tuple);

            parse_row<Index + 1>(s, tuple);
        }
    }

    static void trim(std::string &s)
    {
        const auto is_space = [](char c)
        { return !std::isspace(c); };

        s.erase(s.begin(), std::find_if(s.begin(), s.end(), is_space));
        s.erase(std::find_if(s.rbegin(), s.rend(), is_space).base(), s.end());
    }
};

template<typename... ColType>
class CSV<ColType...>::iterator
{
public:
    using iterator_category = std::input_iterator_tag;
    using value_type        = typename CSV::value_type;
    using difference_type   = std::size_t;

    using pointer   = typename CSV::value_type *;
    using reference = typename CSV::value_type &;

    iterator() : csv_(nullptr)
    {
    }

    explicit iterator(CSV &other) : csv_(other.good() ? &other : nullptr)
    {
        ++(*this);
    }

    inline iterator &operator++()
    {
        if (csv_ != nullptr)
        {
            row_ = csv_->get_row();
            if (!csv_->good()) { csv_ = nullptr; }
        }

        return *this;
    }

    inline iterator operator++(int i)
    {
        iterator tmp = *this;
        ++(*this);
        return tmp;
    }

    inline value_type const &operator*() const { return row_; }

    inline value_type const *operator->() const { return &row_; }

    inline bool operator==(iterator const &other)
    {
        return (this == &other) || (csv_ == nullptr && other.csv_ == nullptr);
    }

    inline bool operator!=(iterator const &other)
    {
        return !(*this == other);
    }

private:
    typename CSV::value_type row_;
    CSV *csv_;
};

The iterator class is essentially a copy of https://github.com/LizardM4/ballin-octo-tribble/blob/master/csv/csv.h , so I'm confused as to why mine doesn't work. I'm somewhat experienced with C++, however not with iterators.

From what I can tell, csv_ is being erroneously set to nullptr, possibly because of in_.good() returning false for some reason? I can't think of anything else. Thanks.

Richard Robinson
  • 867
  • 1
  • 11
  • 38
  • [Edit] the question to include the definitions of `get_row`, the `CSV` constructor, and its missing relevant member variables (like `in_`). – 1201ProgramAlarm Sep 03 '19 at 04:59
  • 1
    Possible duplicate of [Why is iostream::eof inside a loop condition (i.e. \`while (!stream.eof())\`) considered wrong?](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons)—`good` includes `eofbit`. – Davis Herring Sep 03 '19 at 05:36

0 Answers0