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 std::vector<MusingI const *> musings;
65
66 void log(boost::format const & fmt,
67 char const * file, int line);
68 void progress(boost::format const & fmt,
69 char const * file, int line);
70 void warning(boost::format const & fmt,
71 char const * file, int line);
72 void naughty_failure(std::string const & expr, boost::format const & explain,
73 std::string const & file, int line) NORETURN;
74 void error_failure(std::string const & expr, boost::format const & explain,
75 std::string const & file, int line) NORETURN;
76 void invariant_failure(std::string const & expr,
77 std::string const & file, int line) NORETURN;
78 void index_failure(std::string const & vec_expr,
79 std::string const & idx_expr,
80 unsigned long sz,
81 unsigned long idx,
82 std::string const & file, int line) NORETURN;
83 void gasp();
84
85private:
86 std::string do_format(boost::format const & fmt,
87 char const * file, int line);
88};
89
90typedef std::runtime_error oops;
91
92extern sanity global_sanity;
93
94// F is for when you want to build a boost formatter for display
95boost::format F(const char * str);
96
97// FP is for when you want to build a boost formatter for displaying a plural
98boost::format FP(const char * str1, const char * strn, unsigned long count);
99
100// L is for logging, you can log all you want
101#define L(fmt) global_sanity.log(fmt, __FILE__, __LINE__)
102
103// P is for progress, log only stuff which the user might
104// normally like to see some indication of progress of
105#define P(fmt) global_sanity.progress(fmt, __FILE__, __LINE__)
106
107// W is for warnings, which are handled like progress only
108// they are only issued once and are prefixed with "warning: "
109#define W(fmt) global_sanity.warning(fmt, __FILE__, __LINE__)
110
111
112// invariants and assertions
113
114#ifdef __GNUC__
115#define LIKELY(zz) (__builtin_expect((zz), 1))
116#define UNLIKELY(zz) (__builtin_expect((zz), 0))
117#else
118#define LIKELY(zz) (zz)
119#define UNLIKELY(zz) (zz)
120#endif
121
122// I is for invariants that "should" always be true
123// (if they are wrong, there is a *bug*)
124#define I(e) \
125do { \
126 if(UNLIKELY(!(e))) { \
127 global_sanity.invariant_failure("I("#e")", __FILE__, __LINE__); \
128 } \
129} while(0)
130
131// N is for naughtyness on behalf of the user
132// (if they are wrong, the user just did something wrong)
133#define N(e, explain)\
134do { \
135 if(UNLIKELY(!(e))) { \
136 global_sanity.naughty_failure("N("#e")", (explain), __FILE__, __LINE__); \
137 } \
138} while(0)
139
140// E is for errors; they are normal (i.e., not a bug), but not necessarily
141// attributable to user naughtiness
142#define E(e, explain)\
143do { \
144 if(UNLIKELY(!(e))) { \
145 global_sanity.error_failure("E("#e")", (explain), __FILE__, __LINE__); \
146 } \
147} while(0)
148
149
150// we're interested in trapping index overflows early and precisely,
151// because they usually represent *very significant* logic errors. we use
152// an inline template function because the idx(...) needs to be used as an
153// expression, not as a statement.
154
155template <typename T>
156inline T & checked_index(std::vector<T> & v,
157 typename std::vector<T>::size_type i,
158 char const * vec,
159 char const * index,
160 char const * file,
161 int line)
162{
163 if (UNLIKELY(i >= v.size()))
164 global_sanity.index_failure(vec, index, v.size(), i, file, line);
165 return v[i];
166}
167
168template <typename T>
169inline T const & checked_index(std::vector<T> const & v,
170 typename std::vector<T>::size_type i,
171 char const * vec,
172 char const * index,
173 char const * file,
174 int line)
175{
176 if (UNLIKELY(i >= v.size()))
177 global_sanity.index_failure(vec, index, v.size(), i, file, line);
178 return v[i];
179}
180
181#ifdef QA_SUPPORTED
182template <typename T>
183inline T & checked_index(std::vector<T, QA(T)> & v,
184 typename std::vector<T>::size_type i,
185 char const * vec,
186 char const * index,
187 char const * file,
188 int line)
189{
190 if (UNLIKELY(i >= v.size()))
191 global_sanity.index_failure(vec, index, v.size(), i, file, line);
192 return v[i];
193}
194
195template <typename T>
196inline T const & checked_index(std::vector<T, QA(T)> const & v,
197 typename std::vector<T>::size_type i,
198 char const * vec,
199 char const * index,
200 char const * file,
201 int line)
202{
203 if (UNLIKELY(i >= v.size()))
204 global_sanity.index_failure(vec, index, v.size(), i, file, line);
205 return v[i];
206}
207#endif // QA_SUPPORTED
208
209
210#define idx(v, i) checked_index((v), (i), #v, #i, __FILE__, __LINE__)
211
212
213
214// Last gasp dumps
215
216class MusingI
217{
218public:
219 MusingI();
220 virtual ~MusingI();
221 virtual void gasp(std::string & out) const = 0;
222};
223
224
225class MusingBase
226{
227 char const * name;
228 char const * file;
229 char const * func;
230 int line;
231
232protected:
233 MusingBase(char const * name, char const * file, int line, char const * func)
234 : name(name), file(file), func(func), line(line) {}
235
236 void gasp(const std::string & objstr, std::string & out) const;
237};
238
239
240template <typename T>
241class Musing : public MusingI, private MusingBase
242{
243public:
244 Musing(T const & obj, char const * name, char const * file, int line, char const * func)
245 : MusingBase(name, file, line, func), obj(obj) {}
246 virtual void gasp(std::string & out) const;
247private:
248 T const & obj;
249};
250
251
252template <typename T> void
253Musing<T>::gasp(std::string & out) const
254{
255 std::string tmp;
256 dump(obj, tmp);
257
258 MusingBase::gasp(tmp, out);
259}
260
261// Yes, this is insane. No, it doesn't work if you do something more sane.
262// ## explicitly skips macro argument expansion on the things passed to it.
263// Therefore, if we simply did foo ## __LINE__, we would get foo__LINE__ in
264// the output. In fact, even if we did real_M(obj, __LINE__), we would get
265// foo__LINE__ in the output. (## substitutes arguments, but does not expand
266// them.) However, while fake_M does nothing directly, it doesn't pass its
267// line argument to ##; therefore, its line argument is fully expanded before
268// being passed to real_M.
269#ifdef HAVE_TYPEOF
270// even worse, this is g++ only!
271#define real_M(obj, line) Musing<typeof(obj)> this_is_a_musing_fnord_object_ ## line (obj, #obj, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
272#define fake_M(obj, line) real_M(obj, line)
273#define MM(obj) fake_M(obj, __LINE__)
274#else
275#define MM(obj) /* */
276#endif
277
278void dump(std::string const & obj, std::string & out);
279
280#endif // __SANITY_HH__

Archive Download this file

Branches

Tags

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