Program Listing for File errors.hpp
↰ Return to documentation for file (src/navtk/errors.hpp)
#pragma once
#include <mutex>
#include <ostream>
#include <stdexcept>
#include <spdlog/spdlog.h>
// These includes must appear after #include <spdlog/spdlog.h> to prevent linker errors when
// formatting complex types such as std::vector.
#include <spdlog/fmt/bundled/ostream.h>
#include <spdlog/fmt/bundled/ranges.h>
namespace navtk {
enum class ErrorMode {
OFF,
LOG,
DIE
};
std::ostream& operator<<(std::ostream& os, ErrorMode error_mode);
typedef std::runtime_error DefaultLogOrThrowException;
constexpr spdlog::level::level_enum DEFAULT_LOG_OR_THROW_LEVEL = spdlog::level::level_enum::err;
ErrorMode get_global_error_mode();
class ErrorModeLock {
public:
ErrorModeLock(ErrorMode target_mode, bool enable_restore = true);
ErrorModeLock(ErrorModeLock&& src);
ErrorModeLock(const ErrorModeLock&) = delete;
ErrorModeLock& operator=(ErrorModeLock&&) = delete;
ErrorModeLock& operator=(const ErrorModeLock&) = delete;
virtual ~ErrorModeLock();
protected:
void unlock();
void relock();
private:
bool restore_enabled;
bool restore_needed;
std::unique_lock<std::recursive_mutex> lock;
ErrorMode restore_mode;
ErrorMode target_mode;
};
// TODO(PNTOS-387): optional timeout parameter to prevent deadlocks
void set_global_error_mode(ErrorMode mode);
#ifndef NEED_DOXYGEN_EXHALE_WORKAROUND
// we hide the 'full' implementation of log_or_throw in a detail namespace to prevent infinitely
// recursive template evaluation when trying to resolve the overloads -- this allows the other
// overloads to invoke detail::log_or_throw and we know we're always invoking the "main" one.
// End users can also invoke the main one because of the `using detail::log_or_throw` line below.
namespace detail {
#endif
template <typename Exc = DefaultLogOrThrowException,
spdlog::level::level_enum Level = DEFAULT_LOG_OR_THROW_LEVEL,
typename... FormatArgs>
void log_or_throw(ErrorMode mode,
spdlog::format_string_t<FormatArgs...> fmt,
FormatArgs&&... args) {
auto message = fmt::format(fmt, std::forward<FormatArgs>(args)...);
if (mode != ErrorMode::OFF) spdlog::log(Level, "{}", message);
if (mode == ErrorMode::DIE) throw Exc(std::move(message));
}
#ifndef NEED_DOXYGEN_EXHALE_WORKAROUND
}
using detail::log_or_throw;
template <typename Exc = DefaultLogOrThrowException,
spdlog::level::level_enum Level = DEFAULT_LOG_OR_THROW_LEVEL,
typename... FormatArgs>
void log_or_throw(spdlog::format_string_t<FormatArgs...> fmt, FormatArgs&&... args) {
detail::log_or_throw<Exc, Level>(
get_global_error_mode(), fmt, std::forward<FormatArgs>(args)...);
}
template <spdlog::level::level_enum Level, typename... FormatArgs>
void log_or_throw(spdlog::format_string_t<FormatArgs...> fmt, FormatArgs&&... args) {
detail::log_or_throw<DefaultLogOrThrowException, Level>(
get_global_error_mode(), fmt, std::forward<FormatArgs>(args)...);
}
template <spdlog::level::level_enum Level, typename... FormatArgs>
void log_or_throw(ErrorMode mode,
spdlog::format_string_t<FormatArgs...> fmt,
FormatArgs&&... args) {
detail::log_or_throw<DefaultLogOrThrowException, Level>(
mode, fmt, std::forward<FormatArgs>(args)...);
}
// SEE ALSO: py_log_or_throw_ in bindings/python/navtk.cpp, a re-implementation that uses runtime
// values instead of template parameters.
#endif // NEED_DOXYGEN_EXHALE_WORKAROUND
} // namespace navtk