file.h 5.21 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
        }

    template <typename ARG>
        friend
112
        file& operator << (file& f, ARG&& arg);
113
114
115

    template <typename ARG>
        friend
116
        file& operator >> (file& f, ARG&& arg);
117
118

    friend
119
        file& operator << (file& f, std::ostream& (&func) (std::ostream&));
120
121
122
123
124
125
126
127
128
129
130
131
132

    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();
133
           m_impl.read(s, n);
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
           return *this;
       }

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

   int
       get()
       {
           check_state();
149
           int ret = m_impl.get();
150
151
152
153
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
           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)
    {}
};


180
181
182
183
184
namespace std {
    inline
    void getline(file& f, std::string& s) { getline(f.m_impl, s); }
}

185
186
187

#endif