]> git.lyx.org Git - lyx.git/blob - src/support/DebugStream.C
noncopyable + read ChangeLog
[lyx.git] / src / support / DebugStream.C
1 // Created by Lars Gullik Bjønnes
2 // Copyright 1999 Lars Gullik Bjønnes (larsbj@lyx.org)
3 // Released into the public domain.
4
5 // Primarily developed for use in the LyX Project http://www.lyx.org/
6 // but should be adaptable to any project.
7
8 //#define TEST_DEBUGSTREAM
9
10 #include <config.h>
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 //#include "DebugStream.h"
17 #include "debug.h"
18
19 // Since the current C++ lib in egcs does not have a standard implementation
20 // of basic_streambuf and basic_filebuf we don't have to include this
21 // header.
22 //#define MODERN_STL_STREAMS
23 #ifdef MODERN_STL_STREAMS
24 #include <fstream>
25 #endif
26 #include <iostream>
27
28 using std::ostream;
29 using std::streambuf;
30 using std::streamsize;
31 using std::filebuf;
32 using std::cerr;
33 using std::ios;
34
35 ostream & operator<<(ostream & o, Debug::type t)
36 {
37         return o << int(t);
38 }
39
40 /** This is a streambuffer that never prints out anything, at least
41     that is the intention. You can call it a no-op streambuffer, and
42     the ostream that uses it will be a no-op stream.
43 */
44 class nullbuf : public streambuf {
45 protected:
46 #ifndef MODERN_STL_STREAMS
47         typedef char char_type;
48         typedef int int_type;
49         ///
50         virtual int sync() { return 0; }
51 #endif
52         /// 
53         virtual streamsize xsputn(char_type const *, streamsize n) {
54                 // fakes a purge of the buffer by returning n
55                 return n;
56         }
57 #ifdef MODERN_STL_STREAMS
58         ///
59         virtual int_type overflow(int_type c = traits_type::eof()) {
60                 // fakes success by returning c
61                 return c == traits_type::eof() ? ' ' : c;
62         }
63 #else
64         ///
65         virtual int_type overflow(int_type c = EOF) {
66                 // fakes success by returning c
67                 return c == EOF ? ' ' : c;
68         }
69 #endif
70 };
71
72 /** A streambuf that sends the output to two different streambufs. These
73     can be any kind of streambufs.
74 */
75 class teebuf : public streambuf {
76 public:
77         ///
78         teebuf(streambuf * b1, streambuf * b2)
79                 : streambuf(), sb1(b1), sb2(b2) {}
80 protected:
81 #ifdef MODERN_STL_STREAMS
82         ///
83         virtual int sync() {
84                 sb2->pubsync();
85                 return sb1->pubsync();
86         }
87         ///
88         virtual streamsize xsputn(char_type const * p, streamsize n) {
89                 sb2->sputn(p, n);
90                 return sb1->sputn(p, n);
91         }
92         ///
93         virtual int_type overflow(int_type c = traits_type::eof()) {
94                 sb2->sputc(c);
95                 return sb1->sputc(c);
96         }
97 #else
98         typedef char char_type;
99         typedef int int_type;
100         ///
101         virtual int sync() {
102                 sb2->sync();
103                 return sb1->sync();
104         }
105         ///
106         virtual streamsize xsputn(char_type const * p, streamsize n) {
107                 sb2->xsputn(p, n);
108                 return sb1->xsputn(p, n);
109         }
110         ///
111         virtual int_type overflow(int_type c = EOF) {
112                 sb2->overflow(c);
113                 return sb1->overflow(c);
114         }
115 #endif
116 private:
117         ///
118         streambuf * sb1;
119         ///
120         streambuf * sb2;
121 };
122
123 ///
124 class debugbuf : public streambuf {
125 public:
126         ///
127         debugbuf(streambuf * b)
128                 : streambuf(), sb(b) {}
129 protected:
130 #ifdef MODERN_STL_STREAMS
131         ///
132         virtual int sync() {
133                 return sb->pubsync();
134         }
135         ///
136         virtual streamsize xsputn(char_type const * p, streamsize n) {
137                 return sb->sputn(p, n);
138         }
139         ///
140         virtual int_type overflow(int_type c = traits_type::eof()) {
141                 return sb->sputc(c);
142         }
143 #else
144         typedef char char_type;
145         typedef int int_type;
146         ///
147         virtual int sync() {
148                 return sb->sync();
149         }
150         ///
151         virtual streamsize xsputn(char_type const * p, streamsize n) {
152                 return sb->xsputn(p, n);
153         }
154         ///
155         virtual int_type overflow(int_type c = EOF) {
156                 return sb->overflow(c);
157         }
158 #endif
159 private:
160         ///
161         streambuf * sb;
162 };
163
164
165 /// So that public parts of DebugStream does not need to know about filebuf
166 struct DebugStream::debugstream_internal {
167         /// Used when logging to file.
168         filebuf fbuf;
169 };
170
171
172 /// Constructor, sets the debug level to t.
173 DebugStream::DebugStream(Debug::type t)
174         : ostream(new debugbuf(cerr.rdbuf())),
175           dt(t), nullstream(new nullbuf), internal(0) {}
176
177         
178 /// Constructor, sets the log file to f, and the debug level to t.
179 DebugStream::DebugStream(char const * f, Debug::type t)
180         : ostream(new debugbuf(cerr.rdbuf())),
181           dt(t), nullstream(new nullbuf),
182           internal(new debugstream_internal)
183 {
184         internal->fbuf.open(f, ios::out|ios::app);
185         delete rdbuf(new teebuf(cerr.rdbuf(),
186                                 &internal->fbuf));
187 }
188
189
190 DebugStream::~DebugStream()
191 {
192         delete nullstream.rdbuf(0); // Without this we leak
193         delete rdbuf(0);            // Without this we leak
194         delete internal;
195 }
196
197
198 /// Sets the debugstreams' logfile to f.
199 void DebugStream::logFile(char const * f)
200 {
201         if (internal) {
202                 internal->fbuf.close();
203         } else {
204                 internal = new debugstream_internal;
205         }
206         internal->fbuf.open(f, ios::out|ios::app);
207         delete rdbuf(new teebuf(cerr.rdbuf(),
208                                 &internal->fbuf));
209 }
210
211
212 #ifdef TEST_DEBUGSTREAM
213
214 // Example debug stream
215 DebugStream debugstream;
216
217 int main(int, char **)
218 {
219         /**
220            I have been running some tests on this to see how much overhead
221            this kind of permanent debug code has. My conclusion is: not 
222            much. In all, but the most time critical code, this will have 
223            close to no impact at all.
224            
225            In the tests that I have run the use of
226            if (debugstream.debugging(DebugStream::INFO))
227            debugstream << "some debug\n";
228            has close to no overhead when the debug level is not 
229            DebugStream::INFO.
230            
231            The overhead for
232            debugstream.debug(DebugStream::INFO) << "some debug\n";
233            is also very small when the debug level is not
234            DebugStream::INFO. However the overhead for this will increase
235            if complex debugging information is output.
236            
237            The overhead when the debug level is DebugStream::INFO can be
238            significant, but since we then are running in debug mode it is 
239            of no concern.
240            
241            Why should we use this instead of the class Error that we already
242            have? First of all it uses C++ iostream and constructs, secondly
243            it will be a lot easier to output the debug info that we need
244            without a lot of manual conversions, thirdly we can now use 
245            iomanipulators and the complete iostream formatting functions.
246            pluss it will work for all types that have a operator<< 
247            defined, and can be used in functors that take a ostream & as
248            parameter. And there should be less need for temporary objects.
249            And one nice bonus is that we get a log file almost for
250            free.
251            
252            Some of the names are of course open to modifications. I will try
253            to use the names we already use in LyX.
254         */
255         // Just a few simple debugs to show how it can work.
256         debugstream << "Debug level set to Debug::NONE\n";
257         if (debugstream.debugging()) {
258                 debugstream << "Something must be debugged\n";
259         }
260         debugstream.debug(Debug::WARN) << "more debug(WARN)\n";
261         debugstream.debug(Debug::INFO) << "even more debug(INFO)\n";
262         debugstream.debug(Debug::CRIT) << "even more debug(CRIT)\n";
263         debugstream.level(Debug::value("INFO"));
264         debugstream << "Setting debug level to Debug::INFO\n";
265         if (debugstream.debugging()) {
266                 debugstream << "Something must be debugged\n";
267         }
268         debugstream.debug(Debug::WARN) << "more debug(WARN)\n";
269         debugstream.debug(Debug::INFO) << "even more debug(INFO)\n";
270         debugstream.debug(Debug::CRIT) << "even more debug(CRIT)\n";
271         debugstream.addLevel(Debug::type(Debug::CRIT | 
272                                          Debug::WARN));
273         debugstream << "Adding Debug::CRIT and Debug::WARN\n";
274         debugstream[Debug::WARN] << "more debug(WARN)\n";
275         debugstream[Debug::INFO] << "even more debug(INFO)\n";
276         debugstream[Debug::CRIT] << "even more debug(CRIT)\n";
277         debugstream.delLevel(Debug::INFO);
278         debugstream << "Removing Debug::INFO\n";
279         debugstream[Debug::WARN] << "more debug(WARN)\n";
280         debugstream[Debug::INFO] << "even more debug(INFO)\n";
281         debugstream[Debug::CRIT] << "even more debug(CRIT)\n";
282         debugstream.logFile("logfile");
283         debugstream << "Setting logfile to \"logfile\"\n";
284         debugstream << "Value: " << 123 << " " << "12\n";
285         int i = 0;
286         int * p = new int;
287         // note: the (void*) is needed on g++ 2.7.x since it does not
288         // support partial specialization. In egcs this should not be
289         // needed.
290         debugstream << "automatic " << &i 
291                     << ", free store " << p << endl;
292         delete p;
293         /*
294         for (int j = 0; j < 200000; ++j) {
295                 DebugStream tmp;
296                 tmp << "Test" << endl;
297         }
298         */
299 }
300 #endif