file.h 5.65 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Spell-QTL  Software suite for the QTL analysis of modern datasets.
 * Copyright (C) 2016,2017  Damien Leroux <damien.leroux@inra.fr>, Sylvain Jasson <sylvain.jasson@inra.fr>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

18
19
20
21
22
23
24
#ifndef _SPELL_FILE_H_
#define _SPELL_FILE_H_

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

25
26
27
28
extern "C" {
#include <string.h>   /* for strerror */
}

29
#include <fstream>
30
#include <map>  /* for debug */
31
32

struct file {
33
34
35
    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;
36
37
    static size_t open_count;

38
39
    std::fstream m_impl;
    std::string m_path;
40
41
42
43
44
    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]; }
45
46

    struct error : public std::ios_base::failure {
47
        error() : std::ios_base::failure("") {}
48
49
50
51
52
53
        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)
54
        : m_impl(), m_path()
55
    {
56
        open(path, mode);
57
58
    }

59
    ~file() { m_impl.close(); }
60

61
62
63
64
65
66
67
68
69
70
71
72
    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
73
        close() { if (m_impl.is_open()) { m_impl.close(); remove_file(m_path); m_path = "<undefined>"; --open_count; } }
74
75
76
77

    void
        open(const std::string& path, std::ios_base::openmode mode)
        {
78
            m_impl.close();
79
80
            m_path = path;
            m_impl.open(path, mode);
81
82
83
84
85
86
87
            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)));
            }
88
89
        }

90
91
92
93
94
95
    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(); }

96
97
98
    void
        check_state() const
        {
99
100
            if (m_impl.bad()) {
                throw file::error(MESSAGE("Corrupted stream. Can't I/O file " << m_path));
101
102
103
104
            }
            if (m_impl.fail()) {
                throw file::error(MESSAGE("Couldn't I/O file " << m_path));
            }
105
106
107
            /*if (m_impl.eof()) {*/
                /*throw file::error(MESSAGE("Reached EOF while reading file " << m_path));*/
            /*}*/
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
        }

    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();
127
                f.m_impl >> arg;
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
            }
            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();
154
           m_impl.read(s, n);
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
           return *this;
       }

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

   int
       get()
       {
           check_state();
170
           int ret = m_impl.get();
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
           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)
    {}
};


201
202
203
204
205
namespace std {
    inline
    void getline(file& f, std::string& s) { getline(f.m_impl, s); }
}

206
207
208

#endif