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_NS {
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 : a(a)
441 , b(b) {
442 this->setp(nullptr, nullptr);
443 }
444
445 private:
446 auto bool_to_failure(bool B) const noexcept -> int_type {
447 return B ? traits_type::to_int_type(char_type{}) : traits_type::eof();
448 }
449
450 protected:
451 auto imbue(const std::locale& loc) -> void override {
452 a->pubimbue(loc);
453 b->pubimbue(loc);
454 return;
455 }
456
457 auto sync() -> int override { return a->pubsync() | b->pubsync(); }
458
459 auto xsputn(const char_type* s, std::streamsize count)
460 -> std::streamsize override {
461 auto a_ct = a->sputn(s, count);
462 auto b_ct = b->sputn(s, count);
463
464 std::streamsize successful = std::min(a_ct, b_ct);
465
466 if (successful == count) {
467 return count;
468 } else {
469 return 0;
470 }
471 }
472
473 auto overflow(int_type ch) -> int_type override {
474 if (not traits_type::eq_int_type(ch, traits_type::eof())) {
475 auto r_a = a->sputc(traits_type::to_char_type(ch));
476 auto r_b = b->sputc(traits_type::to_char_type(ch));
477 if (traits_type::not_eof(r_a) and traits_type::not_eof(r_b)) {
478 return traits_type::to_int_type({});
479 } else {
480 return traits_type::eof();
481 }
482 }
483 return traits_type::to_int_type({});
484 }
485
486 private:
487 SB1_t* a;
488 SB2_t* b;
489 };
490
491 template <typename Stream>
493 = std::remove_pointer_t<decltype(std::declval<Stream&>().rdbuf())>;
494
495} // namespace detail_io
496
497template <typename StreamA, typename StreamB>
499 : public std::basic_ostream<typename StreamA::char_type,
500 typename StreamA::traits_type> {
501 private:
504 buf_type buf;
505 using ostream_type = std::basic_ostream<typename StreamA::char_type,
506 typename StreamA::traits_type>;
507
508 public:
509 using typename ostream_type::char_type;
510 using typename ostream_type::traits_type;
511
512 using typename ostream_type::int_type;
513 using typename ostream_type::off_type;
514 using typename ostream_type::pos_type;
515
516 basic_teestream(StreamA& a, StreamB& b)
517 : ostream_type(&buf)
518 , buf(a.rdbuf(), b.rdbuf()) {}
519
520 auto rdbuf() const -> buf_type* { return &buf; }
521};
522
523#if 1 || KBLIB_USE_CXX17
524template <typename StreamA, typename StreamB>
525auto tee(StreamA& a, StreamB& b) -> basic_teestream<StreamA, StreamB> {
526 return {a, b};
527}
528#endif
529
530#if KBLIB_USE_CXX17
531
532template <typename F, typename D = std::default_delete<F>,
533 typename P = typename D::pointer>
535 std::filesystem::path path;
536 using pointer = P;
537 void operator()(P fs) {
538 static_cast<D&>(this)(fs);
539 std::filesystem::remove(path);
540 }
541};
542
543# ifdef KBLIB_POSIX_TMPFILE
544namespace detail_io {
545 struct fd_closer {
546 void operator()(int fd) const noexcept { close(fd); }
547 using pointer = int;
548 };
549} // namespace detail_io
550
551using fd_deleter = file_deleter<int, detail_io::fd_closer>;
552# endif
553
554template <typename File = std::fstream>
555[[nodiscard]] auto scoped_file(const std::filesystem::path& path,
556 std::ios_base::openmode mode
557 = std::ios_base::in | std::ios_base::out) {
558 return std::unique_ptr<File, file_deleter<File>>{
559 new std::fstream{path, mode}, {path}};
560}
561
562template <typename File = std::fstream>
563[[nodiscard]] auto tmpfile(const std::filesystem::path& path,
564 std::ios_base::openmode mode
565 = std::ios_base::in | std::ios_base::out) {
566# ifdef KBLIB_POSIX_TMPFILE
567 auto p = std::make_unique<File>(path, mode);
568 std::filesystem::remove(path);
569 return p;
570# else
571 return scoped_file<File>(path, mode);
572# endif
573}
574
575#endif
576
577} // namespace KBLIB_NS
578
579#endif // KBLIB_IO_H
basic_teestream(StreamA &a, StreamB &b)
Definition: io.h:516
auto rdbuf() const -> buf_type *
Definition: io.h:520
auto overflow(int_type ch) -> int_type override
Definition: io.h:473
auto sync() -> int override
Definition: io.h:457
auto imbue(const std::locale &loc) -> void override
Definition: io.h:451
std::basic_streambuf< typename SB1_t::char_type, typename SB1_t::traits_type > base_type
Definition: io.h:423
basic_teestreambuf(SB1_t *a, SB2_t *b)
Definition: io.h:439
auto xsputn(const char_type *s, std::streamsize count) -> std::streamsize override
Definition: io.h:459
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:493
auto to_char_type(IntT ch)
Definition: stringops.h:564
auto to_int_type(CharT ch)
Definition: stringops.h:560
constexpr auto size(const C &c) -> decltype(c.size())
Definition: fakestd.h:366
constexpr struct kblib::nums::min_t min
auto eat_word(std::istream &is) -> std::istream &
Consume all non-spaces to first break, then eat that, too.
Definition: io.h:158
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:525
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_line(string< CharT, O... > &str, CharT delim) -> auto
Read a delimited string into a std::basic_string-like class template.
Definition: io.h:361
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:563
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:555
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
void operator()(P fs)
Definition: io.h:537
std::filesystem::path path
Definition: io.h:535
A helper class for wrapping stream manipulators.
Definition: io.h:185
#define KBLIB_NS
Definition: tdecl.h:113
Contains some type traits not in the standard library that are useful in the implementation of kblib.