kblib 0.2.3
General utilities library for modern C++
io.h
Go to the documentation of this file.
1/* *****************************************************************************
2 * kblib is a general utility library for C++14 and C++17, intended to provide
3 * performant high-level abstractions and more expressive ways to do simple
4 * things.
5 *
6 * Copyright (c) 2021 killerbee
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 * ****************************************************************************/
21
31#ifndef KBLIB_IO_H
32#define KBLIB_IO_H
33
34#include "fakestd.h"
35#include "traits.h"
36
37#include <fstream>
38#include <functional>
39#include <string>
40#include <vector>
41
42#if KBLIB_USE_CXX17
43# include <cstdio>
44# include <filesystem>
45# include <optional>
46
47# if ! defined(_WIN32) \
48 && (defined(__unix__) || defined(__unix) \
49 || (defined(__APPLE__) && defined(__MACH__)))
50/* UNIX-style OS. ------------------------------------------- */
51# include <unistd.h>
52# if defined(_POSIX_VERSION)
53# define KBLIB_POSIX_TMPFILE
54# endif
55# endif
56#endif
57
58#include <iostream>
59
60namespace kblib {
61
62template <typename D = std::string,
63 typename std::enable_if_t<is_contiguous_v<D>, int> = 0>
64auto get_contents(std::istream& in, D& out) -> auto {
65 in.seekg(0, std::ios::end);
66 auto size = in.tellg();
67 out.resize(static_cast<std::size_t>(size));
68 in.seekg(0, std::ios::beg);
69 in.read(reinterpret_cast<char*>(out.data()), size);
70 return size;
71}
72
73template <typename D = std::string,
74 typename std::enable_if_t<not is_contiguous_v<D>, int> = 0>
75auto get_contents(std::istream& in, D& out) -> auto {
76 in.seekg(0, std::ios::end);
77 auto size = in.tellg();
78 out.resize(static_cast<std::size_t>(size));
79 in.seekg(0, std::ios::beg);
80 std::copy((std::istreambuf_iterator<char>(in)),
81 std::istreambuf_iterator<char>(), out.begin());
82 return size;
83}
84
85#if KBLIB_USE_CXX17
96template <typename D = std::string, typename string>
97auto get_file_contents(const string& filename) -> std::optional<D> {
98 static_assert(std::is_trivially_copyable_v<typename D::value_type>,
99 "D must be a sequence of trivial types");
100 static_assert(sizeof(typename D::value_type) == 1,
101 "D must be a sequence of char-sized objects.");
102 std::optional<D> out;
103 if (std::ifstream in(filename, std::ios::in | std::ios::binary); in) {
104 const auto fsize = get_contents(in, out.emplace());
105 if (fsize != to_signed(out->size())) {
106 }
107 }
108 return out;
109}
110#endif
111
122template <typename D = std::string, typename string>
123auto try_get_file_contents(const string& filename) -> D {
124 static_assert(std::is_trivially_copyable<typename D::value_type>::value,
125 "D must be a sequence of trivial types");
126 static_assert(sizeof(typename D::value_type) == 1,
127 "D must be a sequence of char-sized objects.");
128 D out;
129 std::ifstream in(filename, std::ios::in | std::ios::binary);
130 if (in) {
131 in.exceptions(std::ios_base::failbit | std::ios_base::badbit);
132 get_contents(in, out);
133 } else {
134 throw std::system_error(std::make_error_code(std::errc::io_error),
135 "could not open file " + std::string(filename));
136 }
137 return out;
138}
139
146inline auto getline(std::istream& is) -> std::string {
147 std::string ret;
148 std::getline(is, ret);
149 return ret;
150}
151
158inline auto eat_word(std::istream& is) -> std::istream& {
159 do {
160 is.get();
161 } while (is and not std::isspace(is.peek()));
162 return is;
163}
164
173[[deprecated("Use std::ws instead")]] inline std::istream& eat_space(
174 std::istream& is) {
175 while (is and std::isspace(is.peek())) {
176 is.get();
177 }
178 return is;
179}
180
184template <typename F>
185struct get_manip {
186 F _f;
187};
188
211template <typename CharT, typename Traits>
212auto nl(std::basic_istream<CharT, Traits>& is)
213 -> std::basic_istream<CharT, Traits>& {
214 auto n = static_cast<typename Traits::int_type>(is.widen('\n'));
215 for (typename Traits::int_type c = is.peek();
216 is and c != Traits::eof()
217 and std::isspace(static_cast<CharT>(c), is.getloc()) and c != n;
218 c = is.peek()) {
219 is.ignore();
220 }
221 if (is.peek() == n) {
222 is.ignore();
223 }
224 return is;
225}
226
227template <typename T, typename U>
228struct unicode_widen : std::false_type {};
229
230template <>
231struct unicode_widen<char16_t, char32_t> : std::true_type {};
232
233#if KBLIB_USE_CHAR8_T
234
235template <>
236struct unicode_widen<char8_t, char16_t> : std::true_type {};
237
238template <>
239struct unicode_widen<char8_t, char32_t> : std::true_type {};
240
241#endif
242
243#if KBLIB_CHAR_IS_UTF8
244
245template <>
246struct unicode_widen<char, char16_t> : std::true_type {};
247
248template <>
249struct unicode_widen<char, char32_t> : std::true_type {};
250
251#endif
252
253template <typename T, typename U>
254constexpr static bool unicode_widen_v = unicode_widen<T, U>::value;
255
261template <typename CharT>
262auto unformatted_expect(CharT c) -> auto {
263 auto _f = [c](auto& istream) -> decltype(istream) {
264 using SCharT = typename std::decay_t<decltype(istream)>::char_type;
265#if KBLIB_USE_CHAR8_t
266 // clang-format off
267 static_assert(
268 std::is_same_v<CharT, char_type>
269 or (not std::is_same_v<CharT, char8_t>
270 and not std::is_same_v<char_type, char8_t>),
271 "No support for char8_t conversions.");
272 // clang-format on
273#endif
274 auto widen_equal = [&](auto di) {
275 if (di == std::decay_t<decltype(istream)>::traits_type::eof()) {
276 return false;
277 }
278 auto d = static_cast<SCharT>(di);
279 // Feasible:
280 // T -> T : c == d
281 // T -> char : istream.widen(c) == d
282 // u32 -> u16 : c == d
283 // Not currently feasible:
284 // SCharT == char : read multiple chars and convert to CharT?
285 // ... : convert between different wide char types?
286
287 static_assert(
288 (std::is_same<CharT, SCharT>::value
289 or std::is_same<CharT, char>::value),
290 "Stream character type incompatible with argument type.");
291#if KBLIB_USE_CXX17
292# define IF_CONSTEXPR constexpr
293#else
294# define IF_CONSTEXPR
295#endif
296 if IF_CONSTEXPR (std::is_same<CharT, SCharT>::value) {
297 return c == d;
298 } else if IF_CONSTEXPR (unicode_widen_v<CharT, SCharT>) {
299 return c == d;
300 } else {
301 return istream.widen(c) == d;
302 }
303#undef IF_CONSTEXPR
304 };
305
306 if (widen_equal(istream.peek())) {
307 void(istream.get());
308 } else {
309 istream.setstate(std::ios_base::failbit);
310 }
311 return istream;
312 };
313 return get_manip<decltype(_f)>{_f};
314}
315
320template <typename CharT>
321auto expect(CharT c) -> auto {
322 auto _f = [c](auto& istream) -> decltype(istream) {
323 return istream >> std::ws >> unformatted_expect(c);
324 };
325 return get_manip<decltype(_f)>{_f};
326}
327
338template <typename CharT, typename... O,
339 template <typename, typename...> class string>
340inline auto get_line(string<CharT, O...>& str) -> auto {
341 auto _f = [&](auto& istream) -> decltype(istream) {
342 std::getline(istream, str);
343 return istream;
344 };
345 return get_manip<decltype(_f)>{_f};
346}
347
359template <typename CharT, typename... O,
360 template <typename, typename...> class string>
361inline auto get_line(string<CharT, O...>& str, CharT delim) -> auto {
362 auto _f = [&, delim](auto& istream) -> decltype(istream) {
363 std::getline(istream, str, delim);
364 return istream;
365 };
366 return get_manip<decltype(_f)>{_f};
367}
368
372template <typename F, typename CharT, typename Tr>
373std::basic_istream<CharT, Tr>& operator>>(std::basic_istream<CharT, Tr>& is,
374 get_manip<F> func) {
375 return func._f(is);
376}
377
381template <typename F, typename CharT, typename Tr>
382std::basic_ostream<CharT, Tr>& operator<<(std::basic_ostream<CharT, Tr>& is,
383 get_manip<F> func) {
384 return func._f(is);
385}
386
391namespace detail_io {
392
393 /*
394 class steambuf_template : public std::streambuf {
395 protected:
396 auto imbue(const std::locale& loc) -> void override;
397
398 auto setbuf(char_type* s, std::streamsize n) -> std::streambuf* override;
399 auto seekoff(off_type off, std::ios_base::seekdir dir,
400 std::ios_base::openmode which) -> pos_type override; auto seekpos(pos_type
401 pos, std::ios_base::openmode which) -> pos_type override; auto sync() -> int
402 override;
403
404 auto showmanyc() -> std::streamsize override;
405 auto underflow() -> int_type override;
406 auto uflow() -> int_type override;
407 auto xsgetn(char_type* s, std::streamsize count) -> std::streamsize
408 override;
409
410 auto xsputn(const char_type* s, std::streamsize count) -> std::streamsize
411 override; auto overflow(int_type ch) -> int_type override;
412
413 auto pbackfail(int_type c) -> int_type override;
414 };
415 */
416
417 template <typename SB1_t, typename SB2_t>
419 : public std::basic_streambuf<typename SB1_t::char_type,
420 typename SB1_t::traits_type> {
421 public:
422 using base_type = std::basic_streambuf<typename SB1_t::char_type,
423 typename SB1_t::traits_type>;
424 static_assert(std::is_same<typename SB1_t::char_type,
425 typename SB2_t::char_type>::value,
426 "Backing streams must be compatible.");
427 static_assert(std::is_same<typename SB1_t::traits_type,
428 typename SB2_t::traits_type>::value,
429 "Backing streams must be compatible.");
430
431 using typename base_type::char_type;
432 using typename base_type::traits_type;
433
434 using typename base_type::int_type;
435 using typename base_type::off_type;
436 using typename base_type::pos_type;
437
439 basic_teestreambuf(SB1_t* a, SB2_t* b)
440 : buffer(1024)
441 , a(a)
442 , b(b) {
443 this->setp(buffer.data(), buffer.data() + buffer.size() - 1);
444 }
445
446 private:
447 auto flush() -> bool {
448 std::streamsize count = this->pptr() - this->pbase();
449 auto a_ct = a->sputn(this->pbase(), count);
450 auto b_ct = b->sputn(this->pbase(), count);
451
452 std::streamsize successful = std::min(a_ct, b_ct);
453
454 if (successful == count) {
455 this->pbump(static_cast<int>(-count));
456 return true;
457 } else {
458 fail();
459 return false;
460 }
461 }
462
463 auto bool_to_failure(bool B) const noexcept -> int_type {
464 return B ? traits_type::to_int_type(char_type{}) : traits_type::eof();
465 }
466
467 auto fail() noexcept -> void {
468 this->setp(buffer.data(), buffer.data() + buffer.size() - 1);
469 this->pbump(static_cast<int>(buffer.size() - 1));
470 return;
471 }
472
473 protected:
474 auto imbue(const std::locale& loc) -> void override {
475 a->pubimbue(loc);
476 b->pubimbue(loc);
477 return;
478 }
479
480 auto sync() -> int override { return a->pubsync() | b->pubsync(); }
481
482 auto uflow() -> int_type override { return traits_type::eof(); }
483
484 auto xsgetn(char_type*, std::streamsize) -> std::streamsize override {
485 return 0;
486 }
487
488 std::streamsize xsputn(const char_type* s,
489 std::streamsize count) override {
490 bool success = flush();
491 auto a_ct = a->sputn(s, count);
492 auto b_ct = b->sputn(s, count);
493
494 std::streamsize successful = success ? std::min(a_ct, b_ct) : 0;
495
496 if (successful == count) {
497 return count;
498 } else {
499 fail();
500 return 0;
501 }
502 }
503
504 auto overflow(int_type ch) -> int_type override {
505 if (not traits_type::eq_int_type(ch, traits_type::eof())) {
506 traits_type::assign(*this->pptr(), traits_type::to_char_type(ch));
507 this->pbump(1);
508 }
509 return bool_to_failure(flush());
510 }
511
512 private:
513 std::vector<char_type> buffer;
514 SB1_t* a;
515 SB2_t* b;
516 };
517
518 template <typename Stream>
520 = std::remove_pointer_t<decltype(std::declval<Stream&>().rdbuf())>;
521
522} // namespace detail_io
523
524template <typename StreamA, typename StreamB>
526 : public std::basic_ostream<typename StreamA::char_type,
527 typename StreamA::traits_type> {
528 private:
531 buf_type buf;
532 using ostream_type = std::basic_ostream<typename StreamA::char_type,
533 typename StreamA::traits_type>;
534
535 public:
536 using typename ostream_type::char_type;
537 using typename ostream_type::traits_type;
538
539 using typename ostream_type::int_type;
540 using typename ostream_type::off_type;
541 using typename ostream_type::pos_type;
542
543 basic_teestream(StreamA& a, StreamB& b)
544 : ostream_type(&buf)
545 , buf(a.rdbuf(), b.rdbuf()) {}
546
547 auto rdbuf() const -> buf_type* { return &buf; }
548};
549
550#if 1 || KBLIB_USE_CXX17
551template <typename StreamA, typename StreamB>
552auto tee(StreamA& a, StreamB& b) -> basic_teestream<StreamA, StreamB> {
553 return {a, b};
554}
555#endif
556
557#if KBLIB_USE_CXX17
558
559template <typename F, typename D = std::default_delete<F>,
560 typename P = typename D::pointer>
562 std::filesystem::path path;
563 using pointer = P;
564 void operator()(P fs) {
565 static_cast<D&>(this)(fs);
566 std::filesystem::remove(path);
567 }
568};
569
570# ifdef KBLIB_POSIX_TMPFILE
571namespace detail_io {
572 struct fd_closer {
573 void operator()(int fd) const noexcept { close(fd); }
574 using pointer = int;
575 };
576} // namespace detail_io
577
578using fd_deleter = file_deleter<int, detail_io::fd_closer>;
579# endif
580
581template <typename File = std::fstream>
582[[nodiscard]] auto scoped_file(const std::filesystem::path& path,
583 std::ios_base::openmode mode
584 = std::ios_base::in | std::ios_base::out) {
585 return std::unique_ptr<File, file_deleter<File>>{
586 new std::fstream{path, mode}, {path}};
587}
588
589template <typename File = std::fstream>
590[[nodiscard]] auto tmpfile(const std::filesystem::path& path,
591 std::ios_base::openmode mode
592 = std::ios_base::in | std::ios_base::out) {
593# ifdef KBLIB_POSIX_TMPFILE
594 auto p = std::make_unique<File>(path, mode);
595 std::filesystem::remove(path);
596 return p;
597# else
598 return scoped_file<File>(path, mode);
599# endif
600}
601
602#endif
603
604} // namespace kblib
605
606#endif // KBLIB_IO_H
basic_teestream(StreamA &a, StreamB &b)
Definition: io.h:543
auto rdbuf() const -> buf_type *
Definition: io.h:547
auto uflow() -> int_type override
Definition: io.h:482
auto overflow(int_type ch) -> int_type override
Definition: io.h:504
auto sync() -> int override
Definition: io.h:480
auto imbue(const std::locale &loc) -> void override
Definition: io.h:474
std::basic_streambuf< typename SB1_t::char_type, typename SB1_t::traits_type > base_type
Definition: io.h:423
auto xsgetn(char_type *, std::streamsize) -> std::streamsize override
Definition: io.h:484
basic_teestreambuf(SB1_t *a, SB2_t *b)
Definition: io.h:439
std::streamsize xsputn(const char_type *s, std::streamsize count) override
Definition: io.h:488
This header provides some features of C++17 <type_traits> and other headers for C++14,...
#define IF_CONSTEXPR
std::remove_pointer_t< decltype(std::declval< Stream & >().rdbuf())> buf_for
Definition: io.h:520
auto to_char_type(IntT ch)
Definition: stringops.h:564
auto to_int_type(CharT ch)
Definition: stringops.h:560
constexpr struct kblib::nums::min_t min
The main namespace in which all entities from kblib are defined.
Definition: algorithm.h:44
auto eat_word(std::istream &is) -> std::istream &
Consume all non-spaces to first break, then eat that, too.
Definition: io.h:158
constexpr auto size(const C &c) -> decltype(c.size())
Definition: fakestd.h:1070
std::basic_ostream< CharT, Tr > & operator<<(std::basic_ostream< CharT, Tr > &is, get_manip< F > func)
Actually calls the manipulator.
Definition: io.h:382
constexpr auto to_signed(I x) -> std::make_signed_t< I >
Cast integral argument to corresponding signed type.
Definition: fakestd.h:593
auto nl(std::basic_istream< CharT, Traits > &is) -> std::basic_istream< CharT, Traits > &
Read in spaces until the end of the line is found.
Definition: io.h:212
constexpr auto a(const std::initializer_list< T > &a) -> auto
Index an array literal without naming its type.
Definition: simple.h:255
auto try_get_file_contents(const string &filename) -> D
Read the entire contents of a file into a container, such as std::string or std::vector<char>....
Definition: io.h:123
auto tee(StreamA &a, StreamB &b) -> basic_teestream< StreamA, StreamB >
Definition: io.h:552
auto get_contents(std::istream &in, D &out) -> auto
Definition: io.h:64
auto unformatted_expect(CharT c) -> auto
Read a character from an input stream only if it equals c. Acts as an UnformattedInputOperation,...
Definition: io.h:262
auto get_file_contents(const string &filename) -> std::optional< D >
Read the entire contents of a file into a container, such as std::string or std::vector<char>....
Definition: io.h:97
std::istream & eat_space(std::istream &is)
Eat spaces, don't eat an extra.
Definition: io.h:173
auto getline(std::istream &is) -> std::string
By-value std::getline wrapper.
Definition: io.h:146
auto expect(CharT c) -> auto
Read a character from an input stream only if it equals c. Acts as a FormattedInputOperation,...
Definition: io.h:321
auto isspace(char c) -> bool
Definition: stringops.h:331
typename std::decay< T >::type decay_t
Definition: fakestd.h:57
auto tmpfile(const std::filesystem::path &path, std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)
Definition: io.h:590
auto get_line(string< CharT, O... > &str) -> auto
Read a whole line into a std::basic_string-like class template.
Definition: io.h:340
constexpr auto copy(InputIt first, EndIt last, OutputIt out) -> OutputIt
Copies all elements of [first, last) to out. It also allows for a sentinel end iterator.
Definition: algorithm.h:1322
auto scoped_file(const std::filesystem::path &path, std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)
Definition: io.h:582
std::basic_istream< CharT, Tr > & operator>>(std::basic_istream< CharT, Tr > &is, get_manip< F > func)
Actually calls the manipulator.
Definition: io.h:373
KBLIB_CONSTANT auto eof
Names the EOF value for the given character type in std::char_traits.
Definition: traits.h:444
Definition: bits.cpp:73
void operator()(P fs)
Definition: io.h:564
std::filesystem::path path
Definition: io.h:562
A helper class for wrapping stream manipulators.
Definition: io.h:185
Contains some type traits not in the standard library that are useful in the implementation of kblib.