monotone

monotone Mtn Source Tree

Root/sanity.hh

1#ifndef __SANITY_HH__
2#define __SANITY_HH__
3
4// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
5//
6// This program is made available under the GNU GPL version 2.0 or
7// greater. See the accompanying file COPYING for details.
8//
9// This program is distributed WITHOUT ANY WARRANTY; without even the
10// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11// PURPOSE.
12
13#include <stdexcept>
14#include <vector>
15
16#include "boost/circular_buffer.hpp"
17#include "boost/current_function.hpp"
18
19#include "i18n.h"
20#include "quick_alloc.hh" // to get the QA() macro
21#include "numeric_vocab.hh"
22
23// our assertion / sanity / error logging system *was* based on GNU Nana,
24// but we're only using a small section of it, and have anyways rewritten
25// that to use typesafe boost-formatter stuff.
26
27// this is for error messages where we want a clean and inoffensive error
28// message to make it to the user, not a diagnostic error indicating
29// internal failure but a suggestion that they do something differently.
30
31class informative_failure : public std::exception {
32 std::string const whatmsg;
33public:
34 explicit informative_failure(std::string const & s) : whatmsg(s) {};
35 virtual ~informative_failure() throw() {};
36 virtual char const * what() const throw() { return whatmsg.c_str(); }
37};
38
39class MusingI;
40
41class format_base;
42struct plain_format;
43struct i18n_format;
44
45struct sanity {
46 sanity();
47 virtual ~sanity();
48 virtual void initialize(int, char **, char const *);
49 void dump_buffer();
50 void set_debug();
51 void set_quiet();
52 void set_reallyquiet();
53
54 bool debug;
55 bool quiet;
56 bool reallyquiet;
57 boost::circular_buffer<char> logbuf;
58 std::string filename;
59 std::string gasp_dump;
60 bool already_dumping;
61 std::vector<MusingI const *> musings;
62
63 void log(plain_format const & fmt,
64 char const * file, int line);
65 void progress(i18n_format const & fmt,
66 char const * file, int line);
67 void warning(i18n_format const & fmt,
68 char const * file, int line);
69 NORETURN(void naughty_failure(char const * expr, i18n_format const & explain,
70 char const * file, int line));
71 NORETURN(void error_failure(char const * expr, i18n_format const & explain,
72 char const * file, int line));
73 NORETURN(void invariant_failure(char const * expr,
74 char const * file, int line));
75 NORETURN(void index_failure(char const * vec_expr,
76 char const * idx_expr,
77 unsigned long sz,
78 unsigned long idx,
79 char const * file, int line));
80 void gasp();
81
82private:
83 std::string do_format(format_base const & fmt,
84 char const * file, int line);
85 virtual void inform_log(std::string const &msg) = 0;
86 virtual void inform_message(std::string const &msg) = 0;
87 virtual void inform_warning(std::string const &msg) = 0;
88 virtual void inform_error(std::string const &msg) = 0;
89};
90
91extern sanity & global_sanity;
92
93typedef std::runtime_error oops;
94
95// This hides boost::format from infecting every source file. Instead, we
96// implement a single very small formatter.
97
98class
99format_base
100{
101protected:
102 struct impl;
103 impl *pimpl;
104
105 format_base() : pimpl(NULL) {}
106 ~format_base();
107 format_base(format_base const & other);
108 format_base & operator=(format_base const & other);
109 explicit format_base(char const * pattern);
110 explicit format_base(std::string const & pattern);
111 explicit format_base(char const * pattern, std::locale const & loc);
112 explicit format_base(std::string const & pattern, std::locale const & loc);
113public:
114 // It is a lie that these are const; but then, everything about this
115 // class is a lie.
116 std::ostream & get_stream() const;
117 void flush_stream() const;
118 void put_and_flush_signed(s64 const & s) const;
119 void put_and_flush_signed(s32 const & s) const;
120 void put_and_flush_signed(s16 const & s) const;
121 void put_and_flush_signed(s8 const & s) const;
122 void put_and_flush_unsigned(u64 const & u) const;
123 void put_and_flush_unsigned(u32 const & u) const;
124 void put_and_flush_unsigned(u16 const & u) const;
125 void put_and_flush_unsigned(u8 const & u) const;
126 void put_and_flush_float(float const & f) const;
127 void put_and_flush_double(double const & d) const;
128
129 std::string str() const;
130};
131
132
133struct
134plain_format
135 : public format_base
136{
137 plain_format()
138 {}
139
140 explicit plain_format(char const * pattern)
141 : format_base(pattern)
142 {}
143
144 explicit plain_format(std::string const & pattern)
145 : format_base(pattern)
146 {}
147};
148
149template<typename T> inline plain_format const &
150operator %(plain_format const & f, T const & t)
151{
152 f.get_stream() << t;
153 f.flush_stream();
154 return f;
155}
156
157template<typename T> inline plain_format const &
158operator %(const plain_format & f, T & t)
159{
160 f.get_stream() << t;
161 f.flush_stream();
162 return f;
163}
164
165template<typename T> inline plain_format &
166operator %(plain_format & f, T const & t)
167{
168 f.get_stream() << t;
169 f.flush_stream();
170 return f;
171}
172
173template<typename T> inline plain_format &
174operator %(plain_format & f, T & t)
175{
176 f.get_stream() << t;
177 f.flush_stream();
178 return f;
179}
180
181#define SPECIALIZED_OP(format_ty, specialize_arg_ty, overload_arg_ty, s) \
182template <> inline format_ty & \
183operator %<specialize_arg_ty>(format_ty & f, overload_arg_ty & a) \
184{ \
185 f.put_and_flush_ ## s (a); \
186 return f; \
187}
188
189#define ALL_CONST_VARIANTS(fmt_ty, arg_ty, stem) \
190SPECIALIZED_OP( fmt_ty, arg_ty, arg_ty, stem) \
191SPECIALIZED_OP( fmt_ty, arg_ty, const arg_ty, stem) \
192SPECIALIZED_OP(const fmt_ty, arg_ty, arg_ty, stem) \
193SPECIALIZED_OP(const fmt_ty, arg_ty, const arg_ty, stem)
194
195ALL_CONST_VARIANTS(plain_format, s64, signed)
196ALL_CONST_VARIANTS(plain_format, s32, signed)
197ALL_CONST_VARIANTS(plain_format, s16, signed)
198ALL_CONST_VARIANTS(plain_format, s8, signed)
199
200ALL_CONST_VARIANTS(plain_format, u64, unsigned)
201ALL_CONST_VARIANTS(plain_format, u32, unsigned)
202ALL_CONST_VARIANTS(plain_format, u16, unsigned)
203ALL_CONST_VARIANTS(plain_format, u8, unsigned)
204
205ALL_CONST_VARIANTS(plain_format, float, float)
206ALL_CONST_VARIANTS(plain_format, double, double)
207
208
209struct
210i18n_format
211 : public format_base
212{
213 i18n_format() {}
214 explicit i18n_format(const char * localized_pattern);
215 explicit i18n_format(std::string const & localized_pattern);
216};
217
218template<typename T> inline i18n_format const &
219operator %(i18n_format const & f, T const & t)
220{
221 f.get_stream() << t;
222 f.flush_stream();
223 return f;
224}
225
226template<typename T> inline i18n_format const &
227operator %(i18n_format const & f, T & t)
228{
229 f.get_stream() << t;
230 f.flush_stream();
231 return f;
232}
233
234template<typename T> inline i18n_format &
235operator %(i18n_format & f, T const & t)
236{
237 f.get_stream() << t;
238 f.flush_stream();
239 return f;
240}
241
242template<typename T> inline i18n_format &
243operator %(i18n_format & f, T & t)
244{
245 f.get_stream() << t;
246 f.flush_stream();
247 return f;
248}
249
250ALL_CONST_VARIANTS(i18n_format, s64, signed)
251ALL_CONST_VARIANTS(i18n_format, s32, signed)
252ALL_CONST_VARIANTS(i18n_format, s16, signed)
253ALL_CONST_VARIANTS(i18n_format, s8, signed)
254
255ALL_CONST_VARIANTS(i18n_format, u64, unsigned)
256ALL_CONST_VARIANTS(i18n_format, u32, unsigned)
257ALL_CONST_VARIANTS(i18n_format, u16, unsigned)
258ALL_CONST_VARIANTS(i18n_format, u8, unsigned)
259
260ALL_CONST_VARIANTS(i18n_format, float, float)
261ALL_CONST_VARIANTS(i18n_format, double, double)
262
263#undef ALL_CONST_VARIANTS
264#undef SPECIALIZED_OP
265
266std::ostream & operator<<(std::ostream & os, format_base const & fmt);
267
268// F is for when you want to build a boost formatter for display
269i18n_format F(const char * str);
270
271// FP is for when you want to build a boost formatter for displaying a plural
272i18n_format FP(const char * str1, const char * strn, unsigned long count);
273
274// FL is for when you want to build a boost formatter for the developers -- it
275// is not gettextified. Think of the L as "literal" or "log".
276plain_format FL(const char * str);
277
278// L is for logging, you can log all you want
279#define L(fmt) global_sanity.log(fmt, __FILE__, __LINE__)
280
281// P is for progress, log only stuff which the user might
282// normally like to see some indication of progress of
283#define P(fmt) global_sanity.progress(fmt, __FILE__, __LINE__)
284
285// W is for warnings, which are handled like progress only
286// they are only issued once and are prefixed with "warning: "
287#define W(fmt) global_sanity.warning(fmt, __FILE__, __LINE__)
288
289
290// invariants and assertions
291
292#ifdef __GNUC__
293#define LIKELY(zz) (__builtin_expect((zz), 1))
294#define UNLIKELY(zz) (__builtin_expect((zz), 0))
295#else
296#define LIKELY(zz) (zz)
297#define UNLIKELY(zz) (zz)
298#endif
299
300// I is for invariants that "should" always be true
301// (if they are wrong, there is a *bug*)
302#define I(e) \
303do { \
304 if(UNLIKELY(!(e))) { \
305 global_sanity.invariant_failure("I("#e")", __FILE__, __LINE__); \
306 } \
307} while(0)
308
309// N is for naughtyness on behalf of the user
310// (if they are wrong, the user just did something wrong)
311#define N(e, explain)\
312do { \
313 if(UNLIKELY(!(e))) { \
314 global_sanity.naughty_failure("N("#e")", (explain), __FILE__, __LINE__); \
315 } \
316} while(0)
317
318// E is for errors; they are normal (i.e., not a bug), but not necessarily
319// attributable to user naughtiness
320#define E(e, explain)\
321do { \
322 if(UNLIKELY(!(e))) { \
323 global_sanity.error_failure("E("#e")", (explain), __FILE__, __LINE__); \
324 } \
325} while(0)
326
327
328// we're interested in trapping index overflows early and precisely,
329// because they usually represent *very significant* logic errors. we use
330// an inline template function because the idx(...) needs to be used as an
331// expression, not as a statement.
332
333template <typename T>
334inline T & checked_index(std::vector<T> & v,
335 typename std::vector<T>::size_type i,
336 char const * vec,
337 char const * index,
338 char const * file,
339 int line)
340{
341 if (UNLIKELY(i >= v.size()))
342 global_sanity.index_failure(vec, index, v.size(), i, file, line);
343 return v[i];
344}
345
346template <typename T>
347inline T const & checked_index(std::vector<T> const & v,
348 typename std::vector<T>::size_type i,
349 char const * vec,
350 char const * index,
351 char const * file,
352 int line)
353{
354 if (UNLIKELY(i >= v.size()))
355 global_sanity.index_failure(vec, index, v.size(), i, file, line);
356 return v[i];
357}
358
359#ifdef QA_SUPPORTED
360template <typename T>
361inline T & checked_index(std::vector<T, QA(T)> & v,
362 typename std::vector<T>::size_type i,
363 char const * vec,
364 char const * index,
365 char const * file,
366 int line)
367{
368 if (UNLIKELY(i >= v.size()))
369 global_sanity.index_failure(vec, index, v.size(), i, file, line);
370 return v[i];
371}
372
373template <typename T>
374inline T const & checked_index(std::vector<T, QA(T)> const & v,
375 typename std::vector<T>::size_type i,
376 char const * vec,
377 char const * index,
378 char const * file,
379 int line)
380{
381 if (UNLIKELY(i >= v.size()))
382 global_sanity.index_failure(vec, index, v.size(), i, file, line);
383 return v[i];
384}
385#endif // QA_SUPPORTED
386
387
388#define idx(v, i) checked_index((v), (i), #v, #i, __FILE__, __LINE__)
389
390
391
392// Last gasp dumps
393
394class MusingI
395{
396public:
397 MusingI();
398 virtual ~MusingI();
399 virtual void gasp(std::string & out) const = 0;
400};
401
402
403class MusingBase
404{
405 char const * name;
406 char const * file;
407 char const * func;
408 int line;
409
410protected:
411 MusingBase(char const * name, char const * file, int line, char const * func)
412 : name(name), file(file), func(func), line(line) {}
413
414 void gasp_head(std::string & out) const;
415 void gasp_body(const std::string & objstr, std::string & out) const;
416};
417
418
419// remove_reference is a workaround for C++ defect #106.
420template <typename T>
421struct remove_reference {
422 typedef T type;
423};
424
425template <typename T>
426struct remove_reference <T &> {
427 typedef typename remove_reference<T>::type type;
428};
429
430
431template <typename T>
432class Musing : public MusingI, private MusingBase
433{
434public:
435 Musing(typename remove_reference<T>::type const & obj, char const * name, char const * file, int line, char const * func)
436 : MusingBase(name, file, line, func), obj(obj) {}
437 virtual void gasp(std::string & out) const;
438private:
439 typename remove_reference<T>::type const & obj;
440};
441
442// The header line must be printed into the "out" string before
443// dump() is called.
444// This is so that even if the call to dump() throws an error,
445// the header line ("----- begin ...") will be printed.
446// If these calls are collapsed into one, then *no* identifying
447// information will be printed in the case of dump() throwing.
448// Having the header line without the body is still useful, as it
449// provides some semblance of a backtrace.
450template <typename T> void
451Musing<T>::gasp(std::string & out) const
452{
453 std::string tmp;
454 MusingBase::gasp_head(out);
455 dump(obj, tmp);
456
457 MusingBase::gasp_body(tmp, out);
458}
459
460// Yes, this is insane. No, it doesn't work if you do something more sane.
461// ## explicitly skips macro argument expansion on the things passed to it.
462// Therefore, if we simply did foo ## __LINE__, we would get foo__LINE__ in
463// the output. In fact, even if we did real_M(obj, __LINE__), we would get
464// foo__LINE__ in the output. (## substitutes arguments, but does not expand
465// them.) However, while fake_M does nothing directly, it doesn't pass its
466// line argument to ##; therefore, its line argument is fully expanded before
467// being passed to real_M.
468#ifdef HAVE_TYPEOF
469// even worse, this is g++ only!
470#define real_M(obj, line) Musing<typeof(obj)> this_is_a_musing_fnord_object_ ## line (obj, #obj, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
471#define fake_M(obj, line) real_M(obj, line)
472#define MM(obj) fake_M(obj, __LINE__)
473
474// This is to be used for objects that should stay on the musings list
475// even after the caller returns. Note that all PERM_MM objects must
476// be before all MM objects on the musings list, or you will get an
477// invariant failure. (In other words, don't use PERM_MM unless you
478// are sanity::initialize.)
479#define PERM_MM(obj) \
480 new Musing<typeof(obj)>(*(new remove_reference<typeof(obj)>::type(obj)), \
481 #obj, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
482
483#else
484#define MM(obj) /* */
485#define PERM_MM(obj) /* */
486#endif
487
488template <typename T>
489void dump(T const &, std::string &);
490
491template <> void dump(std::string const & obj, std::string & out);
492
493// debugging utility to dump out vars like MM but without requiring a crash
494
495extern void print_var(std::string const & value,
496 std::string const & var,
497 char const * file,
498 int const line,
499 char const * func);
500
501template <typename T> void
502dump(T const & t, char const *var,
503 char const * file, int const line, char const * func)
504{
505 std::string value;
506 dump(t, value);
507 print_var(value, var, file, line, func);
508};
509
510#define DUMP(foo) dump(foo, #foo, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
511
512//////////////////////////////////////////////////////////////////////////
513// Local Variables:
514// mode: C++
515// c-file-style: "gnu"
516// indent-tabs-mode: nil
517// End:
518// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
519//////////////////////////////////////////////////////////////////////////
520
521#endif // __SANITY_HH__

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status