Cloned SEACAS for EXODUS library with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

366 lines
9.9 KiB

// Copyright(C) 1999-2020, 2022 National Technology & Engineering Solutions
// of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
// NTESS, the U.S. Government retains certain rights in this software.
//
// See packages/seacas/LICENSE for details
#if !defined(SMART_ASSERT_H)
#define SMART_ASSERT_H
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#if _MSC_VER > 1000
// note:
// moving this after pragma push will render it useless (VC6)
//
// identifier truncated to 255 chars in debug information
#pragma warning(disable : 4786)
#pragma warning(push)
// *this used in base-member initialization; it's ok
#pragma warning(disable : 4355)
#endif
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
enum {
// default behavior - just loges this assert
// (a message is shown to the user to the console)
lvl_warn = 100,
// default behavior - asks the user what to do:
// Ignore/ Retry/ etc.
lvl_debug = 200,
// default behavior - throws a smart_assert_error
lvl_error = 300,
// default behavior - dumps all assert context to console,
// and aborts
lvl_fatal = 1000
};
/*
contains details about a failed assertion
*/
class assert_context
{
using string = std::string;
public:
assert_context() {}
// where the assertion failed: file & line
void set_file_line(const char *file, int line)
{
file_ = file;
line_ = line;
}
const string &get_context_file() const { return file_; }
int get_context_line() const { return line_; }
// get/ set expression
void set_expr(const string &str) { expr_ = str; }
const string &get_expr() const { return expr_; }
typedef std::pair<string, string> val_and_str;
using vals_array = std::vector<val_and_str>;
// return values array as a vector of pairs:
// [Value, corresponding string]
const vals_array &get_vals_array() const { return vals_; }
// adds one value and its corresponding string
void add_val(const string &val, const string &str) { vals_.push_back(val_and_str(val, str)); }
// get/set level of assertion
void set_level(int nLevel) { level_ = nLevel; }
int get_level() const { return level_; }
// get/set (user-friendly) message
void set_level_msg(const char *strMsg)
{
if (strMsg != nullptr) {
msg_ = strMsg;
}
else {
msg_.erase();
}
}
const string &get_level_msg() const { return msg_; }
private:
// where the assertion occurred
string file_{};
int line_{0};
// expression and values
string expr_{};
vals_array vals_{};
// level and message
int level_{lvl_debug};
string msg_{};
};
namespace smart_assert {
using assert_func = void (*)(const assert_context &);
// helpers
std::string get_typeof_level(int nLevel);
void dump_context_summary(const assert_context &context, std::ostream &out);
void dump_context_detail(const assert_context &context, std::ostream &out);
// defaults
void default_warn_handler(const assert_context &context);
void default_debug_handler(const assert_context &context);
void default_error_handler(const assert_context &context);
void default_fatal_handler(const assert_context &context);
void default_logger(const assert_context &context);
} // namespace smart_assert
namespace Private {
void init_assert();
void set_default_log_stream(std::ostream &out);
void set_default_log_name(const char *str);
// allows finding if a value is of type 'const char *'
// and is null; if so, we cannot print it to an ostream
// directly!!!
template <class T> struct is_null_finder
{
bool is(const T & /*unused*/) const { return false; }
};
template <> struct is_null_finder<char *>
{
bool is(char *const &val) { return val == nullptr; }
};
template <> struct is_null_finder<const char *>
{
bool is(const char *const &val) { return val == nullptr; }
};
} // namespace Private
struct Assert
{
using assert_func = smart_assert::assert_func;
// helpers, in order to be able to compile the code
Assert &SMART_ASSERT_A;
Assert &SMART_ASSERT_B;
explicit Assert(const char *expr)
: SMART_ASSERT_A(*this), SMART_ASSERT_B(*this), needs_handling_(true)
{
context_.set_expr(expr);
if ((logger() == nullptr) || handlers().size() < 4) {
// used before main!
Private::init_assert();
}
}
Assert(const Assert &other)
: SMART_ASSERT_A(*this), SMART_ASSERT_B(*this), context_(other.context_),
needs_handling_(true)
{
other.needs_handling_ = false;
}
~Assert()
{
if (needs_handling_) {
handle_assert();
}
}
template <class type> Assert &print_current_val(const type &val, const char *my_msg)
{
std::ostringstream out;
Private::is_null_finder<type> f;
bool bIsNull = f.is(val);
if (!bIsNull) {
out << val;
}
else {
// null string
out << "null";
}
context_.add_val(out.str(), my_msg);
return *this;
}
Assert &print_context(const char *file, int line)
{
context_.set_file_line(file, line);
return *this;
}
Assert &msg(const char *strMsg)
{
context_.set_level_msg(strMsg);
return *this;
}
Assert &level(int nLevel, const char *strMsg = nullptr)
{
context_.set_level(nLevel);
context_.set_level_msg(strMsg);
return *this;
}
Assert &warn(const char *strMsg = nullptr) { return level(lvl_warn, strMsg); }
Assert &debug(const char *strMsg = nullptr) { return level(lvl_debug, strMsg); }
Assert &error(const char *strMsg = nullptr) { return level(lvl_error, strMsg); }
Assert &fatal(const char *strMsg = nullptr) { return level(lvl_fatal, strMsg); }
// in this case, we set the default logger, and make it
// write everything to this file
static void set_log(const char *strFileName)
{
Private::set_default_log_name(strFileName);
logger() = &smart_assert::default_logger;
}
// in this case, we set the default logger, and make it
// write everything to this log
static void set_log(std::ostream &out)
{
Private::set_default_log_stream(out);
logger() = &smart_assert::default_logger;
}
static void set_log(assert_func log) { logger() = log; }
static void set_handler(int nLevel, assert_func handler) { handlers()[nLevel] = handler; }
private:
// handles the current assertion.
void handle_assert()
{
logger()(context_);
get_handler(context_.get_level())(context_);
}
/*
IMPORTANT NOTE:
The only reason logger & handlers are functions, are
because you might use SMART_ASSERT before main().
In this case, since they're statics, they might not
be initialized. However, making them functions
will make it work.
*/
// the log
static assert_func &logger()
{
static assert_func inst;
return inst;
}
// the handler
typedef std::map<int, assert_func> handlers_collection;
static handlers_collection &handlers()
{
static handlers_collection inst;
return inst;
}
static assert_func get_handler(int nLevel)
{
handlers_collection::const_iterator found = handlers().find(nLevel);
if (found != handlers().end()) {
return (*found).second;
}
// we always assume the debug handler has been set
return (*handlers().find(lvl_debug)).second;
}
private:
assert_context context_{};
mutable bool needs_handling_{true};
};
namespace smart_assert {
inline Assert make_assert(const char *expr) { return Assert(expr); }
} // namespace smart_assert
////////////////////////////////////////////////////////
// macro trickery
// note: NEVER define SMART_ASSERT_DEBUG directly
// (it will be overridden);
//
// #define SMART_ASSERT_DEBUG_MODE instead
#ifdef SMART_ASSERT_DEBUG_MODE
#if SMART_ASSERT_DEBUG_MODE == 1
#define SMART_ASSERT_DEBUG
#else
#undef SMART_ASSERT_DEBUG
#endif
#else
// defaults
#ifndef NDEBUG
#define SMART_ASSERT_DEBUG
#else
#undef SMART_ASSERT_DEBUG
#endif
#endif
#ifdef SMART_ASSERT_DEBUG
// "debug" mode
#define SMART_ASSERT(expr) \
if ((expr)) \
; \
else \
(void)::smart_assert::make_assert(#expr).print_context(__FILE__, __LINE__).SMART_ASSERT_A /**/
#else
// "release" mode
#define SMART_ASSERT(expr) \
if (true) \
; \
else \
(void)::smart_assert::make_assert("").SMART_ASSERT_A /**/
#endif // ifdef SMART_ASSERT_DEBUG
#define SMART_VERIFY(expr) \
if ((expr)) \
; \
else \
(void)::smart_assert::make_assert(#expr) \
.error() \
.print_context(__FILE__, __LINE__) \
.SMART_ASSERT_A /**/
#define SMART_ASSERT_A(x) SMART_ASSERT_OP(x, B)
#define SMART_ASSERT_B(x) SMART_ASSERT_OP(x, A)
#define SMART_ASSERT_OP(x, next) SMART_ASSERT_A.print_current_val((x), #x).SMART_ASSERT_##next /**/
#if _MSC_VER > 1000
#pragma warning(pop)
#endif
#endif