revamped stream errors, add get_line on streams and stream line range

master
Daniel Kolesa 2017-03-02 18:12:00 +01:00
parent f13f11f54e
commit 22a5640a59
4 changed files with 173 additions and 26 deletions

View File

@ -1,5 +1,6 @@
#include <ostd/algorithm.hh>
#include <ostd/string.hh>
#include <ostd/vector.hh>
#include <ostd/io.hh>
using namespace ostd;
@ -34,5 +35,39 @@ int main() {
auto ts2 = make_string(test.iter().take(25));
writefln("-- str beg --\n%s\n-- str end --", ts2);
test.seek(0);
writeln("\n## BY LINE READ ##\n");
for (auto const &line: test.iter_lines()) {
writeln("got line: ", line);
}
test.close();
writeln("\n## FILE SORT ##\n");
wtest.open("test.txt", stream_mode::WRITE);
smpl = "foo\n"
"bar\n"
"baz\n"
"test\n"
"this\n"
"will\n"
"be\n"
"in\n"
"order\n";
copy(iter(smpl), wtest.iter());
wtest.close();
test.open("test.txt");
auto x = make_vector<std::string>(test.iter_lines());
writeln("before sort: ", x);
sort(iter(x));
writeln("after sort: ", x);
return 0;
}

View File

@ -7,6 +7,7 @@
#define OSTD_IO_HH
#include <stdio.h>
#include <cerrno>
#include <vector>
#include <stdexcept>
@ -29,11 +30,6 @@ namespace detail {
};
}
/* TODO: inherit from system_error and come up with the proper infra for it */
struct io_error: std::runtime_error {
using std::runtime_error::runtime_error;
};
struct file_stream: stream {
file_stream(): p_f(), p_owned(false) {}
file_stream(file_stream const &) = delete;
@ -107,7 +103,7 @@ struct file_stream: stream {
if (_fseeki64(p_f, pos, int(whence)))
#endif
{
throw io_error{"file_stream seek error"};
throw stream_error{errno, std::generic_category()};
}
}
@ -118,40 +114,40 @@ struct file_stream: stream {
auto ret = _ftelli64(p_f);
#endif
if (ret < 0) {
throw io_error{"file_stream tell error"};
throw stream_error{errno, std::generic_category()};
}
return ret;
}
void flush() {
if (fflush(p_f)) {
throw io_error{"file_stream flush error"};
throw stream_error{EIO, std::generic_category()};
}
}
void read_bytes(void *buf, size_t count) {
if (fread(buf, 1, count, p_f) != count) {
throw io_error{"file_stream read error"};
throw stream_error{EIO, std::generic_category()};
}
}
void write_bytes(void const *buf, size_t count) {
if (fwrite(buf, 1, count, p_f) != count) {
throw io_error{"file_stream write error"};
throw stream_error{EIO, std::generic_category()};
}
}
int getchar() {
int get_char() {
int ret = fgetc(p_f);
if (ret == EOF) {
throw io_error{"file_stream EOF"};
throw stream_error{EIO, std::generic_category()};
}
return ret;
}
void putchar(int c) {
void put_char(int c) {
if (fputc(c, p_f) == EOF) {
throw io_error{"file_stream EOF"};
throw stream_error{EIO, std::generic_category()};
}
}
@ -189,7 +185,7 @@ namespace detail {
stdout_range() {}
void put(char c) {
if (putchar(c) == EOF) {
throw io_error{"stdout EOF"};
throw stream_error{EIO, std::generic_category()};
}
}
};
@ -201,7 +197,7 @@ namespace detail {
std::is_same_v<std::remove_const_t<range_value_t<R>>, char>
) {
if (fwrite(range.data(), 1, range.size(), stdout) != range.size()) {
throw io_error{"stdout write error"};
throw stream_error{EIO, std::generic_category()};
}
} else {
for (; !range.empty(); range.pop_front()) {
@ -226,7 +222,7 @@ template<typename T>
inline void writeln(T const &v) {
write(v);
if (putchar('\n') == EOF) {
throw io_error{"stdout EOF"};
throw stream_error{EIO, std::generic_category()};
}
}
@ -235,7 +231,7 @@ inline void writeln(T const &v, A const &...args) {
write(v);
write(args...);
if (putchar('\n') == EOF) {
throw io_error{"stdout EOF"};
throw stream_error{EIO, std::generic_category()};
}
}
@ -249,7 +245,7 @@ template<typename ...A>
inline void writefln(string_range fmt, A const &...args) {
writef(fmt, args...);
if (putchar('\n') == EOF) {
throw io_error{"stdout EOF"};
throw stream_error{EIO, std::generic_category()};
}
}

View File

@ -1528,6 +1528,7 @@ struct appender_range: output_range<appender_range<T>> {
}
void clear() { p_data.clear(); }
bool empty() const { return p_data.empty(); }
void reserve(typename T::size_type cap) { p_data.reserve(cap); }
void resize(typename T::size_type len) { p_data.resize(len); }

View File

@ -34,6 +34,13 @@ enum class stream_seek {
template<typename T = char, bool = std::is_pod_v<T>>
struct stream_range;
struct stream_error: std::system_error {
using std::system_error::system_error;
};
template<typename T = char, typename TC = std::basic_string<T>>
struct stream_line_range;
struct stream {
using offset_type = stream_off_t;
@ -63,17 +70,46 @@ struct stream {
virtual void read_bytes(void *, size_t) {}
virtual void write_bytes(void const *, size_t) {}
virtual int getchar() {
virtual int get_char() {
byte c;
read_bytes(&c, 1);
return c;
}
virtual void putchar(int c) {
virtual void put_char(int c) {
byte wc = byte(c);
write_bytes(&wc, 1);
}
template<typename T = char, typename R>
void get_line(R &&writer, bool keep_nl = false) {
bool cr = false;
/* read one char, if it fails to read at all just propagate errors */
T c = get<T>();
bool gotc = false;
do {
if (cr) {
writer.put('\r');
cr = false;
}
if (c == '\r') {
cr = true;
continue;
}
writer.put(c);
gotc = safe_get<T>(c);
} while (gotc && (c != '\n'));
if (cr && (!gotc || keep_nl)) {
/* we had carriage return and either reached EOF
* or were told to keep separator, write the CR
*/
writer.put('\r');
}
if (gotc && keep_nl) {
writer.put('\n');
}
}
template<typename T>
void write(T const &v);
@ -86,14 +122,14 @@ struct stream {
template<typename T>
void writeln(T const &v) {
write(v);
putchar('\n');
put_char('\n');
}
template<typename T, typename ...A>
void writeln(T const &v, A const &...args) {
write(v);
write(args...);
putchar('\n');
put_char('\n');
}
template<typename ...A>
@ -102,12 +138,15 @@ struct stream {
template<typename ...A>
void writefln(string_range fmt, A const &...args) {
writef(fmt, args...);
putchar('\n');
put_char('\n');
}
template<typename T = char>
stream_range<T> iter();
template<typename T = char, typename TC = std::basic_string<T>>
stream_line_range<T, TC> iter_lines(bool keep_nl = false);
template<typename T>
void put(T const *v, size_t count) {
write_bytes(v, count * sizeof(T));
@ -146,6 +185,17 @@ struct stream {
}
private:
/* helper for get_line, so we don't catch errors from output range put */
template<typename T>
bool safe_get(T &c) {
try {
c = get<T>();
return true;
} catch (stream_error const &) {
return false;
}
}
std::locale p_loc;
};
@ -167,7 +217,7 @@ struct stream_range<T, true>: input_range<stream_range<T>> {
if (!p_item.has_value()) {
try {
p_item = p_stream->get<T>();
} catch (...) {
} catch (stream_error const &) {
return true;
}
}
@ -205,7 +255,72 @@ private:
template<typename T>
inline stream_range<T> stream::iter() {
return stream_range<T>(*this);
return stream_range<T>{*this};
}
template<typename T, typename TC>
struct stream_line_range: input_range<stream_line_range<T, TC>> {
using range_category = input_range_tag;
using value_type = TC;
using reference = TC &;
using size_type = size_t;
using difference_type = stream_off_t;
stream_line_range() = delete;
stream_line_range(stream &s, bool keep_nl = false):
p_stream(&s), p_has_item(false), p_keep_nl(keep_nl)
{}
stream_line_range(stream_line_range const &r):
p_stream(r.p_stream), p_item(r.p_item),
p_has_item(r.p_has_item), p_keep_nl(r.p_keep_nl)
{}
bool empty() const {
if (!p_has_item) {
try {
p_item.clear();
p_stream->get_line(p_item, p_keep_nl);
p_has_item = true;
} catch (stream_error const &) {
return true;
}
}
return false;
}
void pop_front() {
if (p_has_item) {
p_item.clear();
p_has_item = false;
} else {
p_stream->get_line(noop_output_range<T>{});
}
}
reference front() const {
if (p_has_item) {
return p_item.get();
} else {
p_stream->get_line(p_item, p_keep_nl);
p_has_item = true;
return p_item.get();
}
}
bool equals_front(stream_line_range const &s) const {
return p_stream == s.p_stream;
}
private:
stream *p_stream;
mutable appender_range<TC> p_item;
mutable bool p_has_item;
bool p_keep_nl;
};
template<typename T, typename TC>
stream_line_range<T, TC> stream::iter_lines(bool keep_nl) {
return stream_line_range<T, TC>{*this, keep_nl};
}
template<typename T>