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 
60 namespace kblib {
61 
62 template <typename D = std::string,
63  typename std::enable_if_t<is_contiguous_v<D>, int> = 0>
64 auto 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 
73 template <typename D = std::string,
74  typename std::enable_if_t<not is_contiguous_v<D>, int> = 0>
75 auto 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
96 template <typename D = std::string, typename string>
97 auto 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 
122 template <typename D = std::string, typename string>
123 auto 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 
146 inline auto getline(std::istream& is) -> std::string {
147  std::string ret;
148  std::getline(is, ret);
149  return ret;
150 }
151 
158 inline 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 
184 template <typename F>
185 struct get_manip {
186  F _f;
187 };
188 
211 template <typename CharT, typename Traits>
212 auto 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 
227 template <typename T, typename U>
228 struct unicode_widen : std::false_type {};
229 
230 template <>
231 struct unicode_widen<char16_t, char32_t> : std::true_type {};
232 
233 #if KBLIB_USE_CHAR8_T
234 
235 template <>
236 struct unicode_widen<char8_t, char16_t> : std::true_type {};
237 
238 template <>
239 struct unicode_widen<char8_t, char32_t> : std::true_type {};
240 
241 #endif
242 
243 #if KBLIB_CHAR_IS_UTF8
244 
245 template <>
246 struct unicode_widen<char, char16_t> : std::true_type {};
247 
248 template <>
249 struct unicode_widen<char, char32_t> : std::true_type {};
250 
251 #endif
252 
253 template <typename T, typename U>
254 constexpr static bool unicode_widen_v = unicode_widen<T, U>::value;
255 
261 template <typename CharT>
262 auto 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 
320 template <typename CharT>
321 auto 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 
338 template <typename CharT, typename... O,
339  template <typename, typename...> class string>
340 inline 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 
359 template <typename CharT, typename... O,
360  template <typename, typename...> class string>
361 inline 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 
372 template <typename F, typename CharT, typename Tr>
373 std::basic_istream<CharT, Tr>& operator>>(std::basic_istream<CharT, Tr>& is,
374  get_manip<F> func) {
375  return func._f(is);
376 }
377 
381 template <typename F, typename CharT, typename Tr>
382 std::basic_ostream<CharT, Tr>& operator<<(std::basic_ostream<CharT, Tr>& is,
383  get_manip<F> func) {
384  return func._f(is);
385 }
386 
391 namespace 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 
438  basic_teestreambuf() = delete;
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>
519  using buf_for
520  = std::remove_pointer_t<decltype(std::declval<Stream&>().rdbuf())>;
521 
522 } // namespace detail_io
523 
524 template <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
551 template <typename StreamA, typename StreamB>
552 auto tee(StreamA& a, StreamB& b) -> basic_teestream<StreamA, StreamB> {
553  return {a, b};
554 }
555 #endif
556 
557 #if KBLIB_USE_CXX17
558 
559 template <typename F, typename D = std::default_delete<F>,
560  typename P = typename D::pointer>
561 struct file_deleter {
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
571 namespace 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 
578 using fd_deleter = file_deleter<int, detail_io::fd_closer>;
579 # endif
580 
581 template <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 
589 template <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:1069
constexpr auto to_signed(I x) -> std::make_signed_t< I >
Cast integral argument to corresponding signed type.
Definition: fakestd.h:592
std::basic_istream< CharT, Tr > & operator>>(std::basic_istream< CharT, Tr > &is, get_manip< F > func)
Actually calls the manipulator.
Definition: io.h:373
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
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
typename std::decay< T >::type decay_t
Definition: fakestd.h:57
std::istream & eat_space(std::istream &is)
Eat spaces, don't eat an extra.
Definition: io.h:173
auto tmpfile(const std::filesystem::path &path, std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out)
Definition: io.h:590
std::basic_ostream< CharT, Tr > & operator<<(std::basic_ostream< CharT, Tr > &is, get_manip< F > func)
Actually calls the manipulator.
Definition: io.h:382
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
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.