Skip to content

Latest commit

 

History

History
184 lines (147 loc) · 4.49 KB

190.md

File metadata and controls

184 lines (147 loc) · 4.49 KB
Info

  • Did you know that C++20 added ostream_joiner that writes successive objects into the basic_ostream separated by a delimiter?

Example

int main() {
  std::vector v{1, 2, 3};
  std::copy(std::cbegin(v), std::cend(v), std::ostream_joiner{std::cout, ", "}); // prints 1, 2, 3
}

https://godbolt.org/z/esavfj

Puzzle

  • Can you implement a simplified version of std::ostream_joiner?
/* TODO - ostream_joiner */

int main() {
  using namespace boost::ut;
  using std::literals::string_literals::operator""s;

  "ostream joiner"_test = [] {
    should("produce an empty") = [stream = std::stringstream{}, v = std::vector<int>{}] {
      std::copy(std::cbegin(v), std::cend(v), ostream_joiner{mut(stream), ""});
      expect(stream.str() == ""s);
    };

    should("join vector elements") = [stream = std::stringstream{}, v = std::vector{'a', 'b', 'c'}] {
      std::copy(std::cbegin(v), std::cend(v), ostream_joiner{mut(stream), ""});
      expect(stream.str() == "abc"s);
    };

    should("join vector elements with comma") = [stream = std::stringstream{}, v = std::vector{1, 2, 3}] {
      std::copy(std::cbegin(v), std::cend(v), ostream_joiner{mut(stream), ", "});
      expect(stream.str() == "1, 2, 3"s);
    };

    should("join array elements with pipe") = [stream = std::stringstream{}, v = std::array{"ab", "cd"}] {
      std::copy(std::cbegin(v), std::cend(v), ostream_joiner{mut(stream), " | "});
      expect(stream.str() == "ab | cd"s);
    };
  };
}

https://godbolt.org/z/vb8jhb

Solutions

struct ostream_joiner{
    ostream_joiner(std::ostream& s, std::string_view separator)
        : s{s}, separator{separator} {}

    auto& operator++() { return *this; }
    auto& operator*() { return *this; }
    auto& operator=(auto&& anything) {
        if (emit_separator)
            s << separator;
        else
            emit_separator = true;
        s << std::forward<decltype(anything)>(anything);
        return *this;
    }

 private:
    std::ostream& s;
    const std::string_view separator;
    bool emit_separator{false};
};

https://godbolt.org/z/hT6j77

struct ostream_joiner {
    ostream_joiner(std::stringstream& ss, std::string_view sep) : incr(false), ss(ss), sep(sep) {}
    template<class T>
    void operator=(T e) {
        if(incr)
            ss << sep;
        incr = false;
        ss << e; }

    ostream_joiner& operator*() { return *this; }
    void operator++() { incr = true; }

    std::stringstream& ss;
    std::string_view sep;
    bool incr;
};

https://godbolt.org/z/M9zjar

template <typename TStream, typename TSep>
struct ostream_joiner {
    ostream_joiner(TStream& stream, TSep sep) : stream_(stream), sep_(std::move(sep)) {}

    auto& operator*() { return *this; }
    auto& operator=(const auto& c) {
        if (not std::exchange(first_, false)) {
            stream_ << sep_;
        }
        stream_ << c;
        return *this;
    }
    auto& operator++() { return *this; }

private:
    TStream& stream_;
    TSep sep_;
    bool first_ = true;
};

https://godbolt.org/z/4zKzeM

struct ostream_joiner {
private:
    std::ostream& output;
    const std::string delimiter;
    bool first{true};

public:
    ostream_joiner(std::ostream& stream, const std::string& delim) : output(stream), delimiter(delim) {}

    template <typename T>
    auto& operator=(T&& input)
    {
        if (first) {
            first = false;
        } else {
            output << delimiter;
        }

        output << std::forward<T>(input);
        return *this;
    }

    auto& operator++() { return *this; }
    auto& operator*() { return *this; }
};

https://godbolt.org/z/johae9

struct ostream_joiner {
    constexpr explicit(true) ostream_joiner(auto& stream, const auto& sep)
        : output_stream(stream)
        , separator(sep)
    {}
    auto operator++() const noexcept { return *this; }
    auto operator*() const noexcept{ return *this; }
    auto operator=(const auto& rhs)
    {
        static bool is_first = true;
        if (not is_first) {
            output_stream << separator;
        } else {
            is_first = false;
        }
        output_stream << rhs;
        return *this;
    }

    std::stringstream& output_stream;
    std::string separator;
};

https://godbolt.org/z/Ke3Gxn