monotone

monotone Mtn Source Tree

Root/sanity.hh

1// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
2#ifndef __SANITY_HH__
3#define __SANITY_HH__
4
5// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
6// all rights reserved.
7// licensed to the public under the terms of the GNU GPL (>= 2)
8// see the file COPYING for details
9
10#include <stdexcept>
11#include <string>
12#include <vector>
13
14#include "boost/format.hpp"
15#include "boost/circular_buffer.hpp"
16#include "boost/current_function.hpp"
17
18#include <config.h> // Required for ENABLE_NLS
19#include "i18n.h"
20#include "ui.hh"
21
22#include "quick_alloc.hh" // to get the QA() macro
23
24#include "paths.hh"
25
26#ifdef __GNUC__
27#define NORETURN __attribute__((noreturn))
28#else
29#define NORETURN
30#endif
31
32// our assertion / sanity / error logging system *was* based on GNU Nana,
33// but we're only using a small section of it, and have anyways rewritten
34// that to use typesafe boost-formatter stuff.
35
36// this is for error messages where we want a clean and inoffensive error
37// message to make it to the user, not a diagnostic error indicating
38// internal failure but a suggestion that they do something differently.
39
40struct informative_failure {
41 informative_failure(std::string const & s) : what(s) {}
42 std::string what;
43};
44
45class MusingI;
46
47struct sanity {
48 sanity();
49 ~sanity();
50 void dump_buffer();
51 void set_debug();
52 void set_brief();
53 void set_quiet();
54 void set_relaxed(bool rel);
55
56 bool debug;
57 bool brief;
58 bool quiet;
59 bool relaxed;
60 boost::circular_buffer<char> logbuf;
61 system_path filename;
62 std::string gasp_dump;
63 bool already_dumping;
64 bool clean_shutdown;
65 std::vector<MusingI const *> musings;
66
67 void log(boost::format const & fmt,
68 char const * file, int line);
69 void progress(boost::format const & fmt,
70 char const * file, int line);
71 void warning(boost::format const & fmt,
72 char const * file, int line);
73 void naughty_failure(std::string const & expr, boost::format const & explain,
74 std::string const & file, int line) NORETURN;
75 void error_failure(std::string const & expr, boost::format const & explain,
76 std::string const & file, int line) NORETURN;
77 void invariant_failure(std::string const & expr,
78 std::string const & file, int line) NORETURN;
79 void index_failure(std::string const & vec_expr,
80 std::string const & idx_expr,
81 unsigned long sz,
82 unsigned long idx,
83 std::string const & file, int line) NORETURN;
84 void gasp();
85
86private:
87 std::string do_format(boost::format const & fmt,
88 char const * file, int line);
89};
90
91typedef std::runtime_error oops;
92
93extern sanity global_sanity;
94
95// F is for when you want to build a boost formatter for display
96boost::format F(const char * str);
97
98// FP is for when you want to build a boost formatter for displaying a plural
99boost::format FP(const char * str1, const char * strn, unsigned long count);
100
101// L is for logging, you can log all you want
102#define L(fmt) global_sanity.log(fmt, __FILE__, __LINE__)
103
104// P is for progress, log only stuff which the user might
105// normally like to see some indication of progress of
106#define P(fmt) global_sanity.progress(fmt, __FILE__, __LINE__)
107
108// W is for warnings, which are handled like progress only
109// they are only issued once and are prefixed with "warning: "
110#define W(fmt) global_sanity.warning(fmt, __FILE__, __LINE__)
111
112
113// invariants and assertions
114
115#ifdef __GNUC__
116#define LIKELY(zz) (__builtin_expect((zz), 1))
117#define UNLIKELY(zz) (__builtin_expect((zz), 0))
118#else
119#define LIKELY(zz) (zz)
120#define UNLIKELY(zz) (zz)
121#endif
122
123// I is for invariants that "should" always be true
124// (if they are wrong, there is a *bug*)
125#define I(e) \
126do { \
127 if(UNLIKELY(!(e))) { \
128 global_sanity.invariant_failure("I("#e")", __FILE__, __LINE__); \
129 } \
130} while(0)
131
132// N is for naughtyness on behalf of the user
133// (if they are wrong, the user just did something wrong)
134#define N(e, explain)\
135do { \
136 if(UNLIKELY(!(e))) { \
137 global_sanity.naughty_failure("N("#e")", (explain), __FILE__, __LINE__); \
138 } \
139} while(0)
140
141// E is for errors; they are normal (i.e., not a bug), but not necessarily
142// attributable to user naughtiness
143#define E(e, explain)\
144do { \
145 if(UNLIKELY(!(e))) { \
146 global_sanity.error_failure("E("#e")", (explain), __FILE__, __LINE__); \
147 } \
148} while(0)
149
150
151// we're interested in trapping index overflows early and precisely,
152// because they usually represent *very significant* logic errors. we use
153// an inline template function because the idx(...) needs to be used as an
154// expression, not as a statement.
155
156template <typename T>
157inline T & checked_index(std::vector<T> & v,
158 typename std::vector<T>::size_type i,
159 char const * vec,
160 char const * index,
161 char const * file,
162 int line)
163{
164 if (UNLIKELY(i >= v.size()))
165 global_sanity.index_failure(vec, index, v.size(), i, file, line);
166 return v[i];
167}
168
169template <typename T>
170inline T const & checked_index(std::vector<T> const & v,
171 typename std::vector<T>::size_type i,
172 char const * vec,
173 char const * index,
174 char const * file,
175 int line)
176{
177 if (UNLIKELY(i >= v.size()))
178 global_sanity.index_failure(vec, index, v.size(), i, file, line);
179 return v[i];
180}
181
182#ifdef QA_SUPPORTED
183template <typename T>
184inline T & checked_index(std::vector<T, QA(T)> & v,
185 typename std::vector<T>::size_type i,
186 char const * vec,
187 char const * index,
188 char const * file,
189 int line)
190{
191 if (UNLIKELY(i >= v.size()))
192 global_sanity.index_failure(vec, index, v.size(), i, file, line);
193 return v[i];
194}
195
196template <typename T>
197inline T const & checked_index(std::vector<T, QA(T)> const & v,
198 typename std::vector<T>::size_type i,
199 char const * vec,
200 char const * index,
201 char const * file,
202 int line)
203{
204 if (UNLIKELY(i >= v.size()))
205 global_sanity.index_failure(vec, index, v.size(), i, file, line);
206 return v[i];
207}
208#endif // QA_SUPPORTED
209
210
211#define idx(v, i) checked_index((v), (i), #v, #i, __FILE__, __LINE__)
212
213
214
215// Last gasp dumps
216
217class MusingI
218{
219public:
220 MusingI();
221 virtual ~MusingI();
222 virtual void gasp(std::string & out) const = 0;
223};
224
225
226class MusingBase
227{
228 char const * name;
229 char const * file;
230 char const * func;
231 int line;
232
233protected:
234 MusingBase(char const * name, char const * file, int line, char const * func)
235 : name(name), file(file), func(func), line(line) {}
236
237 void gasp(const std::string & objstr, std::string & out) const;
238};
239
240
241template <typename T>
242class Musing : public MusingI, private MusingBase
243{
244public:
245 Musing(T const & obj, char const * name, char const * file, int line, char const * func)
246 : MusingBase(name, file, line, func), obj(obj) {}
247 virtual void gasp(std::string & out) const;
248private:
249 T const & obj;
250};
251
252
253template <typename T> void
254Musing<T>::gasp(std::string & out) const
255{
256 std::string tmp;
257 dump(obj, tmp);
258
259 MusingBase::gasp(tmp, out);
260}
261
262// Yes, this is insane. No, it doesn't work if you do something more sane.
263// ## explicitly skips macro argument expansion on the things passed to it.
264// Therefore, if we simply did foo ## __LINE__, we would get foo__LINE__ in
265// the output. In fact, even if we did real_M(obj, __LINE__), we would get
266// foo__LINE__ in the output. (## substitutes arguments, but does not expand
267// them.) However, while fake_M does nothing directly, it doesn't pass its
268// line argument to ##; therefore, its line argument is fully expanded before
269// being passed to real_M.
270#ifdef HAVE_TYPEOF
271// even worse, this is g++ only!
272#define real_M(obj, line) Musing<typeof(obj)> this_is_a_musing_fnord_object_ ## line (obj, #obj, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
273#define fake_M(obj, line) real_M(obj, line)
274#define MM(obj) fake_M(obj, __LINE__)
275#else
276#define MM(obj) /* */
277#endif
278
279void dump(std::string const & obj, std::string & out);
280
281#endif // __SANITY_HH__

Archive Download this file

Branches

Tags

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