Below is the file 'sanity.hh' from this revision. You can also download the file.

// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
#ifndef __SANITY_HH__
#define __SANITY_HH__

// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
// all rights reserved.
// licensed to the public under the terms of the GNU GPL (>= 2)
// see the file COPYING for details

#include <stdexcept>
#include <string>
#include <vector>

#include "boost/format.hpp"
#include "boost/circular_buffer.hpp"
#include "boost/current_function.hpp"

#include <config.h> // Required for ENABLE_NLS
#include "i18n.h"
#include "ui.hh"

#include "quick_alloc.hh" // to get the QA() macro

#include "paths.hh"

#ifdef __GNUC__
#define NORETURN __attribute__((noreturn))
#else
#define NORETURN
#endif

// our assertion / sanity / error logging system *was* based on GNU Nana,
// but we're only using a small section of it, and have anyways rewritten
// that to use typesafe boost-formatter stuff.

// this is for error messages where we want a clean and inoffensive error
// message to make it to the user, not a diagnostic error indicating
// internal failure but a suggestion that they do something differently.

struct informative_failure {
  informative_failure(std::string const & s) : what(s) {}
  std::string what;
};

class MusingI;

struct sanity {
  sanity();
  ~sanity();
  void dump_buffer();
  void set_debug();
  void set_brief();
  void set_quiet();
  void set_relaxed(bool rel);

  bool debug;
  bool brief;
  bool quiet;
  bool relaxed;
  boost::circular_buffer<char> logbuf;
  system_path filename;
  std::string gasp_dump;
  bool already_dumping;
  std::vector<MusingI const *> musings;

  void log(boost::format const & fmt,
           char const * file, int line);
  void progress(boost::format const & fmt,
                char const * file, int line);
  void warning(boost::format const & fmt,
               char const * file, int line);
  void naughty_failure(std::string const & expr, boost::format const & explain,
                       std::string const & file, int line) NORETURN;
  void error_failure(std::string const & expr, boost::format const & explain,
                     std::string const & file, int line) NORETURN;
  void invariant_failure(std::string const & expr,
                         std::string const & file, int line) NORETURN;
  void index_failure(std::string const & vec_expr,
                     std::string const & idx_expr,
                     unsigned long sz,
                     unsigned long idx,
                     std::string const & file, int line) NORETURN;
  void gasp();

private:
  std::string do_format(boost::format const & fmt,
                        char const * file, int line);
};

typedef std::runtime_error oops;

extern sanity global_sanity;

// F is for when you want to build a boost formatter for display
#define F(str) boost::format(gettext(str), get_user_locale())

// FP is for when you want to build a boost formatter for displaying a plural
#define FP(str1, strn, count) boost::format(ngettext(str1, strn, count), get_user_locale())

// L is for logging, you can log all you want
#define L(fmt) global_sanity.log(fmt, __FILE__, __LINE__)

// P is for progress, log only stuff which the user might
// normally like to see some indication of progress of
#define P(fmt) global_sanity.progress(fmt, __FILE__, __LINE__)

// W is for warnings, which are handled like progress only
// they are only issued once and are prefixed with "warning: "
#define W(fmt) global_sanity.warning(fmt, __FILE__, __LINE__)


// invariants and assertions

#ifdef __GNUC__
#define LIKELY(zz) (__builtin_expect((zz), 1))
#define UNLIKELY(zz) (__builtin_expect((zz), 0))
#else
#define LIKELY(zz) (zz)
#define UNLIKELY(zz) (zz)
#endif

// I is for invariants that "should" always be true
// (if they are wrong, there is a *bug*)
#define I(e) \
do { \
  if(UNLIKELY(!(e))) { \
    global_sanity.invariant_failure("I("#e")", __FILE__, __LINE__); \
  } \
} while(0)

// N is for naughtyness on behalf of the user
// (if they are wrong, the user just did something wrong)
#define N(e, explain)\
do { \
  if(UNLIKELY(!(e))) { \
    global_sanity.naughty_failure("N("#e")", (explain), __FILE__, __LINE__); \
  } \
} while(0)

// E is for errors; they are normal (i.e., not a bug), but not necessarily
// attributable to user naughtiness
#define E(e, explain)\
do { \
  if(UNLIKELY(!(e))) { \
    global_sanity.error_failure("E("#e")", (explain), __FILE__, __LINE__); \
  } \
} while(0)


// we're interested in trapping index overflows early and precisely,
// because they usually represent *very significant* logic errors.  we use
// an inline template function because the idx(...) needs to be used as an
// expression, not as a statement.

template <typename T>
inline T & checked_index(std::vector<T> & v,
                         typename std::vector<T>::size_type i,
                         char const * vec,
                         char const * index,
                         char const * file,
                         int line)
{
  if (UNLIKELY(i >= v.size()))
    global_sanity.index_failure(vec, index, v.size(), i, file, line);
  return v[i];
}

template <typename T>
inline T const & checked_index(std::vector<T> const & v,
                               typename std::vector<T>::size_type i,
                               char const * vec,
                               char const * index,
                               char const * file,
                               int line)
{
  if (UNLIKELY(i >= v.size()))
    global_sanity.index_failure(vec, index, v.size(), i, file, line);
  return v[i];
}

#ifdef QA_SUPPORTED
template <typename T>
inline T & checked_index(std::vector<T, QA(T)> & v,
                         typename std::vector<T>::size_type i,
                         char const * vec,
                         char const * index,
                         char const * file,
                         int line)
{
  if (UNLIKELY(i >= v.size()))
    global_sanity.index_failure(vec, index, v.size(), i, file, line);
  return v[i];
}

template <typename T>
inline T const & checked_index(std::vector<T, QA(T)> const & v,
                               typename std::vector<T>::size_type i,
                               char const * vec,
                               char const * index,
                               char const * file,
                               int line)
{
  if (UNLIKELY(i >= v.size()))
    global_sanity.index_failure(vec, index, v.size(), i, file, line);
  return v[i];
}
#endif // QA_SUPPORTED


#define idx(v, i) checked_index((v), (i), #v, #i, __FILE__, __LINE__)



// Last gasp dumps

class MusingI
{
public:
  MusingI();
  virtual ~MusingI();
  virtual void gasp(std::string & out) const = 0;
};


class MusingBase
{
  char const * name;
  char const * file;
  char const * func;
  int line;

protected:
  MusingBase(char const * name, char const * file, int line, char const * func)
    : name(name), file(file), func(func), line(line)  {}

  void gasp(const std::string & objstr, std::string & out) const;
};


template <typename T>
class Musing : public MusingI, private MusingBase
{
public:
  Musing(T const & obj, char const * name, char const * file, int line, char const * func)
    : MusingBase(name, file, line, func), obj(obj) {}
  virtual void gasp(std::string & out) const;
private:
  T const & obj;
};


template <typename T> void
Musing<T>::gasp(std::string & out) const
{
  std::string tmp;
  dump(obj, tmp);

  MusingBase::gasp(tmp, out);
}

// Yes, this is insane.  No, it doesn't work if you do something more sane.
// ## explicitly skips macro argument expansion on the things passed to it.
// Therefore, if we simply did foo ## __LINE__, we would get foo__LINE__ in
// the output.  In fact, even if we did real_M(obj, __LINE__), we would get
// foo__LINE__ in the output.  (## substitutes arguments, but does not expand
// them.)  However, while fake_M does nothing directly, it doesn't pass its
// line argument to ##; therefore, its line argument is fully expanded before
// being passed to real_M.
#ifdef HAVE_TYPEOF
// even worse, this is g++ only!
#define real_M(obj, line) Musing<typeof(obj)> this_is_a_musing_fnord_object_ ## line (obj, #obj, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
#define fake_M(obj, line) real_M(obj, line)
#define MM(obj) fake_M(obj, __LINE__)
#else
#define MM(obj) /* */
#endif

void dump(std::string const & obj, std::string & out);

#endif // __SANITY_HH__