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