file.h 4.84 KB
Newer Older
1
2
3
4
5
6
7
#ifndef _SPELL_FILE_H_
#define _SPELL_FILE_H_

#ifndef MSG_DEBUG
#include <error.h>
#endif

8
9
10
11
extern "C" {
#include <string.h>   /* for strerror */
}

12
#include <fstream>
13
#include <map>  /* for debug */
14
15

struct file {
16
17
18
    static constexpr std::ios_base::seekdir beg = std::ios::beg;
    static constexpr std::ios_base::seekdir cur = std::ios::cur;
    static constexpr std::ios_base::seekdir end = std::ios::end;
19
20
    static size_t open_count;

21
22
    std::fstream m_impl;
    std::string m_path;
23
24
25
26
27
    static std::map<std::string, size_t> debug_open_files;
    static std::mutex dof_mutex;

    static void add_file(const std::string& path) { std::unique_lock<std::mutex> _(dof_mutex); ++debug_open_files[path]; }
    static void remove_file(const std::string& path) { std::unique_lock<std::mutex> _(dof_mutex); --debug_open_files[path]; }
28
29

    struct error : public std::ios_base::failure {
30
        error() : std::ios_base::failure("") {}
31
32
33
34
35
36
        error(const std::string& msg) : std::ios_base::failure(msg) {}
    };

    file() : m_impl(), m_path("<undefined>") {}

    file(const std::string& path, std::ios_base::openmode mode)
37
        : m_impl(), m_path()
38
    {
39
        open(path, mode);
40
41
    }

42
    ~file() { m_impl.close(); }
43

44
45
46
47
48
49
50
51
52
53
54
55
    bool good() const { return m_impl.good(); }
    bool fail() const { return m_impl.fail(); }
    bool eof() const { return m_impl.eof(); }
    bool bad() const { return m_impl.bad(); }

    operator bool () const { return good(); }
    bool operator ! () const { return !good(); }

    operator std::fstream& () { return m_impl; }
    operator std::ostream& () { return m_impl; }

    void
56
        close() { if (m_impl.is_open()) { m_impl.close(); remove_file(m_path); m_path = "<undefined>"; --open_count; } }
57
58
59
60

    void
        open(const std::string& path, std::ios_base::openmode mode)
        {
61
            m_impl.close();
62
63
            m_path = path;
            m_impl.open(path, mode);
64
65
66
67
68
69
70
            if (m_impl.is_open()) {
                check_state();
                ++open_count;
                add_file(path);
            } else {
                throw file::error(MESSAGE("Couldn't open file " << path << " (" << open_count << " already open files). Error message: " << strerror(errno)));
            }
71
72
        }

73
74
75
76
77
78
    std::streampos tellg() { return m_impl.tellg(); }
    std::streampos tellp() { return m_impl.tellp(); }
    file& seekg(std::streamoff off) { m_impl.seekg(off); return *this; }
    file& seekg(std::streamoff off, std::ios_base::seekdir way) { m_impl.seekg(off, way); return *this; }
    int peek() { return m_impl.peek(); }

79
80
81
    void
        check_state() const
        {
82
83
            if (m_impl.bad()) {
                throw file::error(MESSAGE("Corrupted stream. Can't I/O file " << m_path));
84
85
86
87
            }
            if (m_impl.fail()) {
                throw file::error(MESSAGE("Couldn't I/O file " << m_path));
            }
88
89
90
            /*if (m_impl.eof()) {*/
                /*throw file::error(MESSAGE("Reached EOF while reading file " << m_path));*/
            /*}*/
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
        }

    template <typename ARG>
        friend
        file& operator << (file& f, ARG&& arg)
        {
            if (f.m_impl) {
                f.m_impl << arg;
                f.check_state();
            }
            return f;
        }

    template <typename ARG>
        friend
        file& operator >> (file& f, ARG&& arg)
        {
            if (f.m_impl) {
                f.check_state();
110
                f.m_impl >> arg;
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
            }
            return f;
        }

    friend
        file& operator << (file& f, std::ostream& (&func) (std::ostream&))
        {
            if (f.m_impl) {
                f.m_impl << func;
                f.check_state();
            }
            return f;
        }

    file&
        write(const char* s, std::streamsize n)
        {
            m_impl.write(s, n);
            check_state();
            return *this;
        }

   file&
       read(char* s, std::streamsize n)
       {
           check_state();
137
           m_impl.read(s, n);
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
           return *this;
       }

   file&
       put(char c)
       {
           m_impl.put(c);
           check_state();
           return *this;
       }

   int
       get()
       {
           check_state();
153
           int ret = m_impl.get();
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
           return ret;
       }

   template <typename Arg0, typename... Args>
       file&
       get(Arg0& arg, Args&... args)
       {
           m_impl.get(arg, args...);
           check_state();
           return *this;
       }
};


struct ifile : public file {
    ifile(const std::string& path)
        : file(path, std::fstream::in)
    {}

    using file::get;
};


struct ofile : public file {
    ofile(const std::string& path, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::trunc)
        : file(path, mode)
    {}
};


184
185
186
187
188
namespace std {
    inline
    void getline(file& f, std::string& s) { getline(f.m_impl, s); }
}

189
190
191

#endif