1+ #ifndef __STRICT_FSTREAM_HPP
2+ #define __STRICT_FSTREAM_HPP
3+
4+ #include < cassert>
5+ #include < fstream>
6+ #include < cstring>
7+ #include < string>
8+
9+ /* *
10+ * This namespace defines wrappers for std::ifstream, std::ofstream, and
11+ * std::fstream objects. The wrappers perform the following steps:
12+ * - check the open modes make sense
13+ * - check that the call to open() is successful
14+ * - (for input streams) check that the opened file is peek-able
15+ * - turn on the badbit in the exception mask
16+ */
17+ namespace strict_fstream
18+ {
19+
20+ // / Overload of error-reporting function, to enable use with VS.
21+ // / Ref: http://stackoverflow.com/a/901316/717706
22+ static std::string strerror ()
23+ {
24+ std::string buff (80 , ' \0 ' );
25+ #ifdef _WIN32
26+ if (strerror_s (&buff[0 ], buff.size (), errno) != 0 )
27+ {
28+ buff = " Unknown error" ;
29+ }
30+ #elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE || defined(__APPLE__)
31+ // XSI-compliant strerror_r()
32+ if (strerror_r (errno, &buff[0 ], buff.size ()) != 0 )
33+ {
34+ buff = " Unknown error" ;
35+ }
36+ #else
37+ // GNU-specific strerror_r()
38+ auto p = strerror_r (errno, &buff[0 ], buff.size ());
39+ std::string tmp (p, std::strlen (p));
40+ std::swap (buff, tmp);
41+ #endif
42+ buff.resize (buff.find (' \0 ' ));
43+ return buff;
44+ }
45+
46+ // / Exception class thrown by failed operations.
47+ class Exception
48+ : public std::exception
49+ {
50+ public:
51+ Exception (const std::string& msg) : _msg(msg) {}
52+ const char * what () const noexcept { return _msg.c_str (); }
53+ private:
54+ std::string _msg;
55+ }; // class Exception
56+
57+ namespace detail
58+ {
59+
60+ struct static_method_holder
61+ {
62+ static std::string mode_to_string (std::ios_base::openmode mode)
63+ {
64+ static const int n_modes = 6 ;
65+ static const std::ios_base::openmode mode_val_v[n_modes] =
66+ {
67+ std::ios_base::in,
68+ std::ios_base::out,
69+ std::ios_base::app,
70+ std::ios_base::ate,
71+ std::ios_base::trunc,
72+ std::ios_base::binary
73+ };
74+
75+ static const char * mode_name_v[n_modes] =
76+ {
77+ " in" ,
78+ " out" ,
79+ " app" ,
80+ " ate" ,
81+ " trunc" ,
82+ " binary"
83+ };
84+ std::string res;
85+ for (int i = 0 ; i < n_modes; ++i)
86+ {
87+ if (mode & mode_val_v[i])
88+ {
89+ res += (! res.empty ()? " |" : " " );
90+ res += mode_name_v[i];
91+ }
92+ }
93+ if (res.empty ()) res = " none" ;
94+ return res;
95+ }
96+ static void check_mode (const std::string& filename, std::ios_base::openmode mode)
97+ {
98+ if ((mode & std::ios_base::trunc) && ! (mode & std::ios_base::out))
99+ {
100+ throw Exception (std::string (" strict_fstream: open('" ) + filename + " '): mode error: trunc and not out" );
101+ }
102+ else if ((mode & std::ios_base::app) && ! (mode & std::ios_base::out))
103+ {
104+ throw Exception (std::string (" strict_fstream: open('" ) + filename + " '): mode error: app and not out" );
105+ }
106+ else if ((mode & std::ios_base::trunc) && (mode & std::ios_base::app))
107+ {
108+ throw Exception (std::string (" strict_fstream: open('" ) + filename + " '): mode error: trunc and app" );
109+ }
110+ }
111+ static void check_open (std::ios * s_p, const std::string& filename, std::ios_base::openmode mode)
112+ {
113+ if (s_p->fail ())
114+ {
115+ throw Exception (std::string (" strict_fstream: open('" )
116+ + filename + " '," + mode_to_string (mode) + " ): open failed: "
117+ + strerror ());
118+ }
119+ }
120+ static void check_peek (std::istream * is_p, const std::string& filename, std::ios_base::openmode mode)
121+ {
122+ bool peek_failed = true ;
123+ try
124+ {
125+ is_p->peek ();
126+ peek_failed = is_p->fail ();
127+ }
128+ catch (std::ios_base::failure e) {}
129+ if (peek_failed)
130+ {
131+ throw Exception (std::string (" strict_fstream: open('" )
132+ + filename + " '," + mode_to_string (mode) + " ): peek failed: "
133+ + strerror ());
134+ }
135+ is_p->clear ();
136+ }
137+ }; // struct static_method_holder
138+
139+ } // namespace detail
140+
141+ class ifstream
142+ : public std::ifstream
143+ {
144+ public:
145+ ifstream () = default;
146+ ifstream (const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
147+ {
148+ open (filename, mode);
149+ }
150+ void open (const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
151+ {
152+ mode |= std::ios_base::in;
153+ exceptions (std::ios_base::badbit);
154+ detail::static_method_holder::check_mode (filename, mode);
155+ std::ifstream::open (filename, mode);
156+ detail::static_method_holder::check_open (this , filename, mode);
157+ detail::static_method_holder::check_peek (this , filename, mode);
158+ }
159+ }; // class ifstream
160+
161+ class ofstream
162+ : public std::ofstream
163+ {
164+ public:
165+ ofstream () = default ;
166+ ofstream (const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
167+ {
168+ open (filename, mode);
169+ }
170+ void open (const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
171+ {
172+ mode |= std::ios_base::out;
173+ exceptions (std::ios_base::badbit);
174+ detail::static_method_holder::check_mode (filename, mode);
175+ std::ofstream::open (filename, mode);
176+ detail::static_method_holder::check_open (this , filename, mode);
177+ }
178+ }; // class ofstream
179+
180+ class fstream
181+ : public std::fstream
182+ {
183+ public:
184+ fstream () = default ;
185+ fstream (const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
186+ {
187+ open (filename, mode);
188+ }
189+ void open (const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
190+ {
191+ if (! (mode & std::ios_base::out)) mode |= std::ios_base::in;
192+ exceptions (std::ios_base::badbit);
193+ detail::static_method_holder::check_mode (filename, mode);
194+ std::fstream::open (filename, mode);
195+ detail::static_method_holder::check_open (this , filename, mode);
196+ detail::static_method_holder::check_peek (this , filename, mode);
197+ }
198+ }; // class fstream
199+
200+ } // namespace strict_fstream
201+
202+ #endif
0 commit comments