introduce try_invoke -- a logging exception catcher
This commit is contained in:
parent
3d9dcbcf87
commit
f1b5633ce6
130
plugins/windows-notification/api/include/ginvoke.hpp
Normal file
130
plugins/windows-notification/api/include/ginvoke.hpp
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
|
||||||
|
#ifndef GINVOKE_HPP
|
||||||
|
#define GINVOKE_HPP
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <utility>
|
||||||
|
#include <optional>
|
||||||
|
#include <variant>
|
||||||
|
#include <iterator>
|
||||||
|
#include <functional>
|
||||||
|
#include <exception>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "overload.hpp"
|
||||||
|
#include "make_array.hpp"
|
||||||
|
#include "hexify.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace glib {
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
using static_c_str = const char *;
|
||||||
|
using varstring_t = std::variant<std::string, static_c_str>;
|
||||||
|
struct varstring : varstring_t
|
||||||
|
{
|
||||||
|
const char* c_str() const && = delete;
|
||||||
|
const char* c_str() const &
|
||||||
|
{
|
||||||
|
return std::visit(overload{
|
||||||
|
[](const std::string &s){ return s.c_str(); },
|
||||||
|
[](const static_c_str s){ return s; }
|
||||||
|
}, static_cast<const varstring_t &>(*this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hresult
|
||||||
|
{
|
||||||
|
std::int32_t code;
|
||||||
|
varstring message;
|
||||||
|
};
|
||||||
|
std::optional<hresult> get_if_hresult_error(std::exception_ptr) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OStream, typename T>
|
||||||
|
inline auto &describe_argument(OStream &s, const T &a) { return s << a; }
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, std::string_view const a) { return s << std::quoted(a); }
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const std::string & a) { return s << std::quoted(a); }
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const char * const a) { return s << std::quoted(a); }
|
||||||
|
// TODO: overload for const GString *
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, std::wstring_view const a) { return s << std::quoted(a); }
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const std::wstring & a) { return s << std::quoted(a); }
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const wchar_t * const a) = delete; // not implemented
|
||||||
|
// TODO: handle wide strings maybe
|
||||||
|
|
||||||
|
template<typename... Arg>
|
||||||
|
inline impl::varstring describe_arguments(const Arg &...a) noexcept try
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
((describe_argument(ss,a)<<','), ...);
|
||||||
|
auto s = std::move(ss).str();
|
||||||
|
s.pop_back();
|
||||||
|
return {s};
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return {"<failed to stringify>"};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define FORMAT "%s(%s) failed: %s"
|
||||||
|
template<typename... Arg>
|
||||||
|
inline void log_invocation_failure(const char *e, const char *func_name, const Arg &...a) noexcept
|
||||||
|
{
|
||||||
|
const auto args = describe_arguments(a...);
|
||||||
|
g_warning(FORMAT, func_name, args.c_str(), e);
|
||||||
|
}
|
||||||
|
template<typename... Arg>
|
||||||
|
inline void log_invocation_failure_desc(const char* e, const char* e_desc, const char* func_name, const Arg&... a) noexcept
|
||||||
|
{
|
||||||
|
const auto args = describe_arguments(a...);
|
||||||
|
g_warning(FORMAT": %s", func_name, args.c_str(), e, e_desc);
|
||||||
|
}
|
||||||
|
#undef FORMAT
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Invokable, typename... Arg>
|
||||||
|
inline gboolean try_invoke(const char *func_name, Invokable &&i, const Arg &...a) noexcept try
|
||||||
|
{
|
||||||
|
return std::invoke(std::forward<Invokable>(i), a...);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
log_invocation_failure(e.what(), func_name, a...);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
if (const auto e = impl::get_if_hresult_error(std::current_exception()))
|
||||||
|
{
|
||||||
|
auto hr = make_array("hresult 0x01234567\0");
|
||||||
|
hexify32(static_cast<std::uint32_t>(e->code), std::end(hr)-1);
|
||||||
|
log_invocation_failure_desc(std::begin(hr), e->message.c_str(), func_name, a...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log_invocation_failure("unknown error", func_name, a...);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace glib
|
||||||
|
|
||||||
|
|
||||||
|
#define g_try_invoke(invokable, ...) glib::try_invoke(#invokable, invokable, __VA_ARGS__)
|
||||||
|
|
||||||
|
#endif
|
12
plugins/windows-notification/api/include/hexify.hpp
Normal file
12
plugins/windows-notification/api/include/hexify.hpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#ifndef HEXIFY_HPP
|
||||||
|
#define HEXIFY_HPP
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
constexpr void hexify32(std::uint32_t val, char *const end) noexcept
|
||||||
|
{
|
||||||
|
auto p = end-1;
|
||||||
|
for (auto i = 0; i < 32/4; ++i, --p, val >>= 4)
|
||||||
|
*p = "0123456789ABCDEF"[val & ((1u<<4)-1u)];
|
||||||
|
}
|
||||||
|
#endif
|
15
plugins/windows-notification/api/include/make_array.hpp
Normal file
15
plugins/windows-notification/api/include/make_array.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
#ifndef MAKE_ARRAY_HPP
|
||||||
|
#define MAKE_ARRAY_HPP
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
inline auto make_array(const char (&from_literal)[N]) noexcept
|
||||||
|
{
|
||||||
|
static_assert( N );
|
||||||
|
std::array<char,N-1> a;
|
||||||
|
std::copy(+from_literal, from_literal+a.size(), a.begin());
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
#endif
|
10
plugins/windows-notification/api/include/overload.hpp
Normal file
10
plugins/windows-notification/api/include/overload.hpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
#ifndef OVERLOAD_HPP
|
||||||
|
#define OVERLOAD_HPP
|
||||||
|
template<typename... Callable>
|
||||||
|
struct overload : Callable...
|
||||||
|
{
|
||||||
|
overload( Callable &&...c ) : Callable{std::move(c)}... {}
|
||||||
|
using Callable::operator()...;
|
||||||
|
};
|
||||||
|
#endif
|
35
plugins/windows-notification/api/src/ginvoke.cpp
Normal file
35
plugins/windows-notification/api/src/ginvoke.cpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
#include "ginvoke.hpp"
|
||||||
|
#include "converter.hpp"
|
||||||
|
|
||||||
|
#include <winrt/base.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace glib::impl
|
||||||
|
{
|
||||||
|
std::optional<hresult> get_if_hresult_error(const std::exception_ptr p) noexcept try
|
||||||
|
{
|
||||||
|
std::rethrow_exception(p);
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error& e)
|
||||||
|
{
|
||||||
|
const char *ptr = nullptr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ptr = wsview_to_char(e.message());
|
||||||
|
std::string msg{ptr};
|
||||||
|
g_free(const_cast<char *>(ptr)); // WTF? Deletion is not modification!
|
||||||
|
return {{ e.code(), {std::move(msg)} }};
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
g_free(const_cast<char *>(ptr));
|
||||||
|
return {{ e.code(), {"<failed to stringify>"} }};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// This is not the exception you are looking for.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue