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