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.
230 lines
6.0 KiB
230 lines
6.0 KiB
2 years ago
|
// Copyright(C) 1999-2021, 2023 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
|
||
|
|
||
|
#include "smart_assert.h"
|
||
|
|
||
|
#include <cstdlib>
|
||
|
#include <fstream>
|
||
|
#include <set>
|
||
|
#include <sstream>
|
||
|
#include <stdexcept>
|
||
|
|
||
|
void break_into_debugger() { std::cerr << "Not Implemented.\n"; }
|
||
|
|
||
|
namespace {
|
||
|
// in case we're logging using the default logger...
|
||
|
struct stream_holder
|
||
|
{
|
||
|
stream_holder() = default;
|
||
|
~stream_holder()
|
||
|
{
|
||
|
if (owns_) {
|
||
|
delete out_;
|
||
|
}
|
||
|
out_ = nullptr;
|
||
|
}
|
||
|
std::ostream *out_{nullptr};
|
||
|
bool owns_{false};
|
||
|
};
|
||
|
// information about the stream we write to, in case
|
||
|
// we're using the default logger
|
||
|
stream_holder default_logger_info;
|
||
|
|
||
|
// initializes the SMART_ASSERT library
|
||
|
[[maybe_unused]] struct assert_initializer
|
||
|
{
|
||
|
assert_initializer() { Private::init_assert(); }
|
||
|
} init;
|
||
|
} // anonymous namespace
|
||
|
|
||
|
namespace smart_assert {
|
||
|
|
||
|
// returns a message corresponding to the type of level
|
||
|
std::string get_typeof_level(int nLevel)
|
||
|
{
|
||
|
switch (nLevel) {
|
||
|
case lvl_warn: return "Warning";
|
||
|
case lvl_debug: return "Assertion failed";
|
||
|
case lvl_error: return "Assertion failed (Error)";
|
||
|
case lvl_fatal: return "Assertion failed (FATAL)";
|
||
|
default: {
|
||
|
std::ostringstream out;
|
||
|
out << "Assertion failed (level=" << nLevel << ")";
|
||
|
return out.str();
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// helpers, for dumping the assertion context
|
||
|
void dump_context_summary(const assert_context &context, std::ostream &out)
|
||
|
{
|
||
|
out << "\n"
|
||
|
<< get_typeof_level(context.get_level()) << " in " << context.get_context_file() << ":"
|
||
|
<< context.get_context_line() << '\n';
|
||
|
if (!context.get_level_msg().empty()) {
|
||
|
// we have a user-friendly message
|
||
|
out << context.get_level_msg();
|
||
|
}
|
||
|
else {
|
||
|
out << "\nExpression: " << context.get_expr();
|
||
|
}
|
||
|
out << '\n';
|
||
|
}
|
||
|
|
||
|
void dump_context_detail(const assert_context &context, std::ostream &out)
|
||
|
{
|
||
|
out << "\n"
|
||
|
<< get_typeof_level(context.get_level()) << " in " << context.get_context_file() << ":"
|
||
|
<< context.get_context_line() << '\n';
|
||
|
if (!context.get_level_msg().empty()) {
|
||
|
out << "User-friendly msg: '" << context.get_level_msg() << "'\n";
|
||
|
}
|
||
|
out << "\nExpression: '" << context.get_expr() << "'\n";
|
||
|
|
||
|
typedef assert_context::vals_array ac_vals_array;
|
||
|
const ac_vals_array &aVals = context.get_vals_array();
|
||
|
if (!aVals.empty()) {
|
||
|
bool bFirstTime = true;
|
||
|
ac_vals_array::const_iterator first = aVals.begin(), last = aVals.end();
|
||
|
while (first != last) {
|
||
|
if (bFirstTime) {
|
||
|
out << "Values: ";
|
||
|
bFirstTime = false;
|
||
|
}
|
||
|
else {
|
||
|
out << " ";
|
||
|
}
|
||
|
out << first->second << "='" << first->first << "'\n";
|
||
|
++first;
|
||
|
}
|
||
|
}
|
||
|
out << '\n';
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////
|
||
|
// logger
|
||
|
|
||
|
void default_logger(const assert_context &context)
|
||
|
{
|
||
|
if (default_logger_info.out_ == nullptr) {
|
||
|
return;
|
||
|
}
|
||
|
dump_context_detail(context, *(default_logger_info.out_));
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////
|
||
|
// handlers
|
||
|
|
||
|
// warn : just dump summary to console
|
||
|
void default_warn_handler(const assert_context &context)
|
||
|
{
|
||
|
dump_context_summary(context, std::cout);
|
||
|
}
|
||
|
|
||
|
// debug: ask user what to do
|
||
|
void default_debug_handler(const assert_context &context)
|
||
|
{
|
||
|
#if 1
|
||
|
dump_context_detail(context, std::cerr);
|
||
|
abort();
|
||
|
#else
|
||
|
static bool ignore_all = false;
|
||
|
if (ignore_all)
|
||
|
// ignore All asserts
|
||
|
return;
|
||
|
typedef std::pair<std::string, int> file_and_line;
|
||
|
static std::set<file_and_line> ignorer;
|
||
|
if (ignorer.find(file_and_line(context.get_context_file(), context.get_context_line())) !=
|
||
|
ignorer.end())
|
||
|
// this is Ignored Forever
|
||
|
return;
|
||
|
|
||
|
dump_context_summary(context, std::cerr);
|
||
|
std::cerr << "\nPress (I)gnore/ Ignore (F)orever/ Ignore (A)ll/ (D)ebug/ A(b)ort: ";
|
||
|
std::cerr.flush();
|
||
|
char ch = 0;
|
||
|
|
||
|
bool bContinue = true;
|
||
|
while (bContinue && std::cin.get(ch)) {
|
||
|
bContinue = false;
|
||
|
switch (ch) {
|
||
|
case 'i':
|
||
|
case 'I':
|
||
|
// ignore
|
||
|
break;
|
||
|
|
||
|
case 'f':
|
||
|
case 'F':
|
||
|
// ignore forever
|
||
|
ignorer.insert(file_and_line(context.get_context_file(), context.get_context_line()));
|
||
|
break;
|
||
|
|
||
|
case 'a':
|
||
|
case 'A':
|
||
|
// ignore all
|
||
|
ignore_all = true;
|
||
|
break;
|
||
|
|
||
|
case 'd':
|
||
|
case 'D':
|
||
|
// break
|
||
|
break_into_debugger();
|
||
|
break;
|
||
|
|
||
|
case 'b':
|
||
|
case 'B': abort(); break;
|
||
|
|
||
|
default: bContinue = true; break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// error : throw a runtime exception
|
||
|
void default_error_handler(const assert_context &context)
|
||
|
{
|
||
|
std::ostringstream out;
|
||
|
dump_context_summary(context, out);
|
||
|
throw std::runtime_error(out.str());
|
||
|
}
|
||
|
|
||
|
// fatal : dump error and abort
|
||
|
void default_fatal_handler(const assert_context &context)
|
||
|
{
|
||
|
dump_context_detail(context, std::cerr);
|
||
|
abort();
|
||
|
}
|
||
|
|
||
|
} // namespace smart_assert
|
||
|
|
||
|
namespace Private {
|
||
|
|
||
|
void init_assert()
|
||
|
{
|
||
|
Assert::set_log(&::smart_assert::default_logger);
|
||
|
Assert::set_handler(lvl_warn, &::smart_assert::default_warn_handler);
|
||
|
Assert::set_handler(lvl_debug, &::smart_assert::default_debug_handler);
|
||
|
Assert::set_handler(lvl_error, &::smart_assert::default_error_handler);
|
||
|
Assert::set_handler(lvl_fatal, &::smart_assert::default_fatal_handler);
|
||
|
}
|
||
|
|
||
|
// sets the default logger to write to this stream
|
||
|
void set_default_log_stream(std::ostream &out)
|
||
|
{
|
||
|
default_logger_info.out_ = &out;
|
||
|
default_logger_info.owns_ = false;
|
||
|
}
|
||
|
|
||
|
// sets the default logger to write to this file
|
||
|
void set_default_log_name(const char *str)
|
||
|
{
|
||
|
default_logger_info.owns_ = false;
|
||
|
default_logger_info.out_ = new std::ofstream(str);
|
||
|
default_logger_info.owns_ = true;
|
||
|
}
|
||
|
|
||
|
} // namespace Private
|