/* * 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 */ #pragma once #include #include #include /////////////////////////////////////////////////////////////////////////////// // 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 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>; if constexpr (std::is_enum_v) { unhandled (static_cast> (t)); } else { ::panic (fmt::format ("unhandled value: {}", t)); } } } template constexpr T unhandled [[noreturn]] (T &&t) noexcept { cruft::debug::detail::unhandled (std::forward (t)); }