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

Archive Download this file

Branches

Tags

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