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

Archive Download this file

Branches

Tags

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