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