revamped stream errors, add get_line on streams and stream line range
parent
f13f11f54e
commit
22a5640a59
|
@ -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;
|
||||
}
|
||||
|
|
34
ostd/io.hh
34
ostd/io.hh
|
@ -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()};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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); }
|
||||
|
|
129
ostd/stream.hh
129
ostd/stream.hh
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue