Eclipse SUMO - Simulation of Urban MObility
strict_fstream.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <cassert>
4 #include <fstream>
5 #include <cstring>
6 #include <string>
7 #include <vector>
8 
17 namespace strict_fstream
18 {
19 
20 // Help people out a bit, it seems like this is a common recommenation since
21 // musl breaks all over the place.
22 #if defined(__NEED_size_t) && !defined(__MUSL__)
23 #warning "It seems to be recommended to patch in a define for __MUSL__ if you use musl globally: https://www.openwall.com/lists/musl/2013/02/10/5"
24 #define __MUSL__
25 #endif
26 
27 // Workaround for broken musl implementation
28 // Since musl insists that they are perfectly compatible, ironically enough,
29 // they don't officially have a __musl__ or similar. But __NEED_size_t is defined in their
30 // relevant header (and not in working implementations), so we can use that.
31 #ifdef __MUSL__
32 #warning "Working around broken strerror_r() implementation in musl, remove when musl is fixed"
33 #endif
34 
35 // Non-gnu variants of strerror_* don't necessarily null-terminate if
36 // truncating, so we have to do things manually.
37 inline std::string trim_to_null(const std::vector<char> &buff)
38 {
39  std::string ret(buff.begin(), buff.end());
40 
41  const std::string::size_type pos = ret.find('\0');
42  if (pos == std::string::npos) {
43  ret += " [...]"; // it has been truncated
44  } else {
45  ret.resize(pos);
46  }
47  return ret;
48 }
49 
54 static std::string strerror()
55 {
56  // Can't use std::string since we're pre-C++17
57  std::vector<char> buff(256, '\0');
58 
59 #ifdef _WIN32
60  // Since strerror_s might set errno itself, we need to store it.
61  const int err_num = errno;
62  if (strerror_s(buff.data(), buff.size(), err_num) != 0) {
63  return trim_to_null(buff);
64  } else {
65  return "Unknown error (" + std::to_string(err_num) + ")";
66  }
67 #elif ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || defined(__APPLE__)) && ! _GNU_SOURCE) || defined(__MUSL__)
68 // XSI-compliant strerror_r()
69  const int err_num = errno; // See above
70  if (strerror_r(err_num, buff.data(), buff.size()) == 0) {
71  return trim_to_null(buff);
72  } else {
73  return "Unknown error (" + std::to_string(err_num) + ")";
74  }
75 #else
76 // GNU-specific strerror_r()
77  char * p = strerror_r(errno, &buff[0], buff.size());
78  return std::string(p, std::strlen(p));
79 #endif
80 }
81 
83 class Exception
84  : public std::exception
85 {
86 public:
87  Exception(const std::string& msg) : _msg(msg) {}
88  const char * what() const noexcept { return _msg.c_str(); }
89 private:
90  std::string _msg;
91 }; // class Exception
92 
93 namespace detail
94 {
95 
97 {
98  static std::string mode_to_string(std::ios_base::openmode mode)
99  {
100  static const int n_modes = 6;
101  static const std::ios_base::openmode mode_val_v[n_modes] =
102  {
103  std::ios_base::in,
104  std::ios_base::out,
105  std::ios_base::app,
106  std::ios_base::ate,
107  std::ios_base::trunc,
108  std::ios_base::binary
109  };
110 
111  static const char * mode_name_v[n_modes] =
112  {
113  "in",
114  "out",
115  "app",
116  "ate",
117  "trunc",
118  "binary"
119  };
120  std::string res;
121  for (int i = 0; i < n_modes; ++i)
122  {
123  if (mode & mode_val_v[i])
124  {
125  res += (! res.empty()? "|" : "");
126  res += mode_name_v[i];
127  }
128  }
129  if (res.empty()) res = "none";
130  return res;
131  }
132  static void check_mode(const std::string& filename, std::ios_base::openmode mode)
133  {
134  if ((mode & std::ios_base::trunc) && ! (mode & std::ios_base::out))
135  {
136  throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and not out");
137  }
138  else if ((mode & std::ios_base::app) && ! (mode & std::ios_base::out))
139  {
140  throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: app and not out");
141  }
142  else if ((mode & std::ios_base::trunc) && (mode & std::ios_base::app))
143  {
144  throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and app");
145  }
146  }
147  static void check_open(std::ios * s_p, const std::string& filename, std::ios_base::openmode mode)
148  {
149  if (s_p->fail())
150  {
151  throw Exception(std::string("strict_fstream: open('")
152  + filename + "'," + mode_to_string(mode) + "): open failed: "
153  + strerror());
154  }
155  }
156  static void check_peek(std::istream * is_p, const std::string& filename, std::ios_base::openmode mode)
157  {
158  bool peek_failed = true;
159  try
160  {
161  is_p->peek();
162  peek_failed = is_p->fail();
163  }
164  catch (const std::ios_base::failure &) {}
165  if (peek_failed)
166  {
167  throw Exception(std::string("strict_fstream: open('")
168  + filename + "'," + mode_to_string(mode) + "): peek failed: "
169  + strerror());
170  }
171  is_p->clear();
172  }
173 }; // struct static_method_holder
174 
175 } // namespace detail
176 
177 class ifstream
178  : public std::ifstream
179 {
180 public:
181  ifstream() = default;
182  ifstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
183  {
184  open(filename, mode);
185  }
186  void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
187  {
188  mode |= std::ios_base::in;
189  exceptions(std::ios_base::badbit);
191  std::ifstream::open(filename, mode);
192  detail::static_method_holder::check_open(this, filename, mode);
193  detail::static_method_holder::check_peek(this, filename, mode);
194  }
195 }; // class ifstream
196 
197 class ofstream
198  : public std::ofstream
199 {
200 public:
201  ofstream() = default;
202  ofstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
203  {
204  open(filename, mode);
205  }
206  void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
207  {
208  mode |= std::ios_base::out;
209  exceptions(std::ios_base::badbit);
211  std::ofstream::open(filename, mode);
212  detail::static_method_holder::check_open(this, filename, mode);
213  }
214 }; // class ofstream
215 
216 class fstream
217  : public std::fstream
218 {
219 public:
220  fstream() = default;
221  fstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
222  {
223  open(filename, mode);
224  }
225  void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
226  {
227  if (! (mode & std::ios_base::out)) mode |= std::ios_base::in;
228  exceptions(std::ios_base::badbit);
230  std::fstream::open(filename, mode);
231  detail::static_method_holder::check_open(this, filename, mode);
232  detail::static_method_holder::check_peek(this, filename, mode);
233  }
234 }; // class fstream
235 
236 } // namespace strict_fstream
Exception class thrown by failed operations.
Exception(const std::string &msg)
const char * what() const noexcept
fstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
void open(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
ifstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
void open(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
void open(const std::string &filename, std::ios_base::openmode mode=std::ios_base::out)
ofstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::out)
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.hpp:21838
std::string trim_to_null(const std::vector< char > &buff)
static std::string strerror()
static void check_mode(const std::string &filename, std::ios_base::openmode mode)
static void check_open(std::ios *s_p, const std::string &filename, std::ios_base::openmode mode)
static std::string mode_to_string(std::ios_base::openmode mode)
static void check_peek(std::istream *is_p, const std::string &filename, std::ios_base::openmode mode)