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

Archive Download this file

Branches

Tags

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