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

Archive Download this file

Branches

Tags

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