debug: move panic related calls to a distinct unit

This will allow us to reduce the required headers and avoid some
circular dependencies in some client libraries.
This commit is contained in:
Danny Robson 2019-05-17 10:48:29 +10:00
parent 644cae506f
commit a4d963e00b
4 changed files with 148 additions and 131 deletions

View File

@ -278,6 +278,8 @@ list (
cpuid.hpp
debug.cpp
debug.hpp
debug/panic.cpp
debug/panic.hpp
encode/number.hpp
encode/base.cpp
encode/base.hpp

133
debug.hpp
View File

@ -3,12 +3,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2010-2017 Danny Robson <danny@nerdcruft.net>
* Copyright 2010-2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "platform.hpp"
#include "debug/panic.hpp"
//#include "maths.hpp" // XXX: See notes at the end of file for maths.hpp inclusion
#include <cmath>
@ -435,136 +436,6 @@ namespace cruft::debug {
void breakpoint (void);
///////////////////////////////////////////////////////////////////////////////
// We define an identity macro for panic so that TCL and GCC don't decide to
// fuck us over by redefine the symbol.
//
// Apparently this can't be taken care of via a '-Dpanic=panic' argument,
// because it just ignores it for reasons I can't figure out.
#ifdef panic
#undef panic
#endif
#define panic panic
//-----------------------------------------------------------------------------
namespace cruft::debug::detail {
void
panic [[noreturn]] (const char *msg);
void not_implemented [[noreturn]] (const char *msg);
void unreachable [[noreturn]] (const char *msg);
}
//-----------------------------------------------------------------------------
constexpr void
panic [[noreturn]] (const char *msg)
{
! msg
? panic (msg)
: cruft::debug::detail::panic (msg);
}
//-----------------------------------------------------------------------------
inline void
panic [[noreturn]] (const std::string &msg)
{
panic (msg.c_str ());
}
///////////////////////////////////////////////////////////////////////////////
// not_implemented/unreachable/panic must be callable from constexpr contexts.
// but they rely on functions that aren't constexpr to perform the controlled
// abort.
//
// we can work around this in the same way assert does by using a conditional
// that hides an extern function that actually does the work. as we can't
// utilise external state this has to be the message variable which will
// assume isn't ever null.
//
// to avoid warnings about a return from a noreturn function we recurse into
// ourself in the alternate case. this branch shouldn't ever be taken, but if
// it is we were planning on crashing anyway...
///////////////////////////////////////////////////////////////////////////////
constexpr void
not_implemented [[noreturn]] (const char *msg)
{
! msg
? not_implemented (msg)
: cruft::debug::detail::not_implemented (msg);
}
constexpr void
not_implemented [[noreturn]] (void)
{
not_implemented ("operation not implemented");
}
constexpr void unimplemented [[noreturn]] (void) { not_implemented (); }
constexpr void unimplemented [[noreturn]] (const char *msg) { not_implemented (msg); }
///////////////////////////////////////////////////////////////////////////////
constexpr void
unreachable [[noreturn]] (const char *msg)
{
! msg
? unreachable (msg)
: cruft::debug::detail::unreachable (msg);
}
constexpr void
unreachable [[noreturn]] (void)
{
unreachable ("unreachable code isn't");
}
///////////////////////////////////////////////////////////////////////////////
/// report a fatal error induced by an unhandled value, especially in switch
/// statements. will almost invariably abort the application.
namespace cruft::debug::detail {
template <typename T>
T
unhandled [[noreturn]] (T &&t) noexcept
{
// If we've been given an enum we should handle it like its underlying
// type so that when we end up logging it we don't accidentally
// trigger an infinite loop of calls to `unhandled` within the
// ostream operator. It doesn't have to be pretty, just functional.
using base_type = std::remove_reference_t<std::decay_t<T>>;
if constexpr (std::is_enum_v<base_type>) {
unhandled (static_cast<std::underlying_type_t<base_type>> (t));
} else {
std::ostringstream os;
os << "unhandled value: " << t << '\n';
::panic (os.str ());
}
}
}
template <typename T>
constexpr T
unhandled [[noreturn]] (T &&t) noexcept
{
cruft::debug::detail::unhandled (std::forward<T> (t));
}
///////////////////////////////////////////////////////////////////////////////
void warn (void);
void warn (const std::string&);

1
debug/panic.cpp Normal file
View File

@ -0,0 +1 @@
#include "panic.hpp"

143
debug/panic.hpp Normal file
View File

@ -0,0 +1,143 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2010-2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include <type_traits>
#include <sstream>
#include <utility>
///////////////////////////////////////////////////////////////////////////////
// We define an identity macro for panic so that TCL and GCC don't decide to
// fuck us over by redefine the symbol.
//
// Apparently this can't be taken care of via a '-Dpanic=panic' argument,
// because it just ignores it for reasons I can't figure out.
#ifdef panic
#undef panic
#endif
#define panic panic
//-----------------------------------------------------------------------------
namespace cruft::debug::detail {
void
panic [[noreturn]] (const char *msg);
void not_implemented [[noreturn]] (const char *msg);
void unreachable [[noreturn]] (const char *msg);
}
//-----------------------------------------------------------------------------
constexpr void
panic [[noreturn]] (const char *msg)
{
! msg
? panic (msg)
: cruft::debug::detail::panic (msg);
}
//-----------------------------------------------------------------------------
inline void
panic [[noreturn]] (const std::string &msg)
{
panic (msg.c_str ());
}
///////////////////////////////////////////////////////////////////////////////
// not_implemented/unreachable/panic must be callable from constexpr contexts.
// but they rely on functions that aren't constexpr to perform the controlled
// abort.
//
// we can work around this in the same way assert does by using a conditional
// that hides an extern function that actually does the work. as we can't
// utilise external state this has to be the message variable which will
// assume isn't ever null.
//
// to avoid warnings about a return from a noreturn function we recurse into
// ourself in the alternate case. this branch shouldn't ever be taken, but if
// it is we were planning on crashing anyway...
///////////////////////////////////////////////////////////////////////////////
constexpr void
not_implemented [[noreturn]] (const char *msg)
{
! msg
? not_implemented (msg)
: cruft::debug::detail::not_implemented (msg);
}
constexpr void
not_implemented [[noreturn]] (void)
{
not_implemented ("operation not implemented");
}
constexpr void unimplemented [[noreturn]] (void) { not_implemented (); }
constexpr void unimplemented [[noreturn]] (const char *msg) { not_implemented (msg); }
///////////////////////////////////////////////////////////////////////////////
constexpr void
unreachable [[noreturn]] (const char *msg)
{
! msg
? unreachable (msg)
: cruft::debug::detail::unreachable (msg);
}
constexpr void
unreachable [[noreturn]] (void)
{
unreachable ("unreachable code isn't");
}
///////////////////////////////////////////////////////////////////////////////
/// report a fatal error induced by an unhandled value, especially in switch
/// statements. will almost invariably abort the application.
namespace cruft::debug::detail {
template <typename T>
T
unhandled [[noreturn]] (T &&t) noexcept
{
// If we've been given an enum we should handle it like its underlying
// type so that when we end up logging it we don't accidentally
// trigger an infinite loop of calls to `unhandled` within the
// ostream operator. It doesn't have to be pretty, just functional.
using base_type = std::remove_reference_t<std::decay_t<T>>;
if constexpr (std::is_enum_v<base_type>) {
unhandled (static_cast<std::underlying_type_t<base_type>> (t));
} else {
std::ostringstream os;
os << "unhandled value: " << t << '\n';
::panic (os.str ());
}
}
}
template <typename T>
constexpr T
unhandled [[noreturn]] (T &&t) noexcept
{
cruft::debug::detail::unhandled (std::forward<T> (t));
}