From 783391494430390fbb4553539d8bbf30318e11c6 Mon Sep 17 00:00:00 2001 From: Christopher Di Bella Date: Mon, 28 Dec 2020 20:36:45 -0800 Subject: [PATCH] adds `ranges::basic_unformatted_istream_view` `istream_view` is only useful if the input to be formatted on read and `std::istreambuf_iterator` only works with types that have a `std::char_traits` specialisation. The purpose of `unformatted_istream_view` is to read from an istream object without formatting (so integers will be read as binary rather than as text). --- .gitignore | 3 + include/range/v3/view.hpp | 1 + include/range/v3/view/unformatted_istream.hpp | 119 ++++++++++++++++++ test/view/CMakeLists.txt | 1 + test/view/unformatted_istream.cpp | 88 +++++++++++++ 5 files changed, 212 insertions(+) create mode 100644 include/range/v3/view/unformatted_istream.hpp create mode 100644 test/view/unformatted_istream.cpp diff --git a/.gitignore b/.gitignore index fb7c170200..82c38465cd 100644 --- a/.gitignore +++ b/.gitignore @@ -237,3 +237,6 @@ $RECYCLE.BIN/ \#*# .#* /CMakeSettings.json + +# clangd cache +.cache/clangd/ diff --git a/include/range/v3/view.hpp b/include/range/v3/view.hpp index 51cd2016f9..1bbb34fd3d 100644 --- a/include/range/v3/view.hpp +++ b/include/range/v3/view.hpp @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include diff --git a/include/range/v3/view/unformatted_istream.hpp b/include/range/v3/view/unformatted_istream.hpp new file mode 100644 index 0000000000..29dd9a1f8b --- /dev/null +++ b/include/range/v3/view/unformatted_istream.hpp @@ -0,0 +1,119 @@ +/// \file +// Range v3 library +// +// Copyright Eric Niebler 2013-present +// Copyright Google LLC 2020-present +// +// Use, modification and distribution is subject to the +// Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Project home: https://github.com/ericniebler/range-v3 +// + +#ifndef RANGES_V3_VIEW_UNFORMATTED_ISTREAM_HPP +#define RANGES_V3_VIEW_UNFORMATTED_ISTREAM_HPP + +#include + +#include + +#include +#include +#include +#include + +#include + +namespace ranges +{ + /// \addtogroup group-views + /// @{ + template> + struct basic_unformatted_istream_view + : view_facade, unknown> + { + private: + friend range_access; + std::basic_istream * sin_; + semiregular_box_t obj_; + struct cursor + { + private: + friend range_access; + using single_pass = std::true_type; + basic_unformatted_istream_view * rng_ = nullptr; + + public: + cursor() = default; + explicit cursor(basic_unformatted_istream_view * rng) noexcept + : rng_(rng) + {} + void next() + { + rng_->next(); + } + Val & read() const noexcept + { + return rng_->cached(); + } + bool equal(default_sentinel_t) const noexcept + { + return !rng_->sin_; + } + bool equal(cursor that) const noexcept + { + return !rng_->sin_ == !that.rng_->sin_; + } + }; + void next() + { + if(!sin_->read(reinterpret_cast(std::addressof(cached())), sizeof(Val))) { + sin_ = nullptr; + } + } + cursor begin_cursor() + { + return cursor{this}; + } + + public: + basic_unformatted_istream_view() = default; + explicit basic_unformatted_istream_view(std::istream & sin) + noexcept(std::is_nothrow_default_constructible::value) + : sin_(&sin) + , obj_{} + { + // The current `value_` is considered stale, so we'd better update it (otherwise the + // first value in the range will *always* be `Val()`.) + next(); + } + Val & cached() noexcept + { + return obj_; + } + }; + + /// \cond + namespace _unformatted_istream_ + { + /// \endcond + template(typename Val, typename CharT, typename Traits)( + /// \pre + requires copy_constructible AND default_constructible) + inline basic_unformatted_istream_view + unformatted_istream_view(std::basic_istream & sin) + { + return basic_unformatted_istream_view{sin}; + } + /// \cond + } // namespace _unformatted_istream_ + using namespace _unformatted_istream_; + /// \endcond + /// @} +} // namespace ranges + +#include + +#endif diff --git a/test/view/CMakeLists.txt b/test/view/CMakeLists.txt index 338e9985b4..e9aef8dc2a 100644 --- a/test/view/CMakeLists.txt +++ b/test/view/CMakeLists.txt @@ -62,6 +62,7 @@ rv3_add_test(test.view.take_while view.take_while take_while.cpp) rv3_add_test(test.view.tokenize view.tokenize tokenize.cpp) rv3_add_test(test.view.transform view.transform transform.cpp) rv3_add_test(test.view.trim view.trim trim.cpp) +rv3_add_test(test.view.unformatted_istream view.unformatted_istream unformatted_istream.cpp) rv3_add_test(test.view.unique view.unique unique.cpp) rv3_add_test(test.view.view view.view view.cpp) rv3_add_test(test.view.zip view.zip zip.cpp) diff --git a/test/view/unformatted_istream.cpp b/test/view/unformatted_istream.cpp new file mode 100644 index 0000000000..30c97f3d60 --- /dev/null +++ b/test/view/unformatted_istream.cpp @@ -0,0 +1,88 @@ +// Range v3 library +// +// Copyright Eric Niebler 2014-present +// Copyright Google LLC 2020-present +// +// Use, modification and distribution is subject to the +// Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Project home: https://github.com/ericniebler/range-v3 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../simple_test.hpp" +#include "../test_utils.hpp" + +namespace rv = ranges::views; + +namespace std { + template + std::ostream& operator<<(std::ostream& os, std::vector const& v) + { + os << std::hex; + ranges::copy(v, ::ranges::ostream_iterator(os, " ")); + return os; + } +} + +int main() +{ + { +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + auto const input = std::vector(3, "\x21\x22\x23\x24"); +#else + auto const input = std::vector{ + "\x21\x22\x23\x24", + "\x22\x21\x24\x23", + "\x24\x23\x22\x21", + }; +#endif // endianness + { + auto in = std::istringstream(input[0]); + auto const expected = std::vector{0x21, 0x22, 0x23, 0x24}; + auto const actual = ranges::unformatted_istream_view(in) | ranges::to; + CHECK(actual == expected); + } + + { + auto in = std::istringstream(input[1]); + auto const expected = std::vector{0x2122, 0x2324}; + auto const actual = ranges::unformatted_istream_view(in) | ranges::to; + CHECK(actual == expected); + } + { + auto in = std::istringstream(input[2]); + auto const expected = std::vector{0x21222324}; + auto const actual = ranges::unformatted_istream_view(in) | ranges::to; + CHECK(actual == expected); + } + { + auto in = std::istringstream(input[2]); + auto const actual = ranges::unformatted_istream_view(in) | ranges::to; + CHECK(in.fail()); + } +#if __cplusplus >= 201703L + { + auto in = std::istringstream(input[2]); + auto const expected = std::vector{0x1.4446480000000000179Dp-61f}; + auto const actual = ranges::unformatted_istream_view(in) | ranges::to; + CHECK(actual == expected); + } +#endif // __cplusplus >= 201703L + } + + return ::test_result(); +}