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