From f1b5633ce612c9040a1fe18a5468e40f3737ad82 Mon Sep 17 00:00:00 2001 From: mjk Date: Fri, 5 Mar 2021 20:23:23 +0000 Subject: [PATCH] introduce try_invoke -- a logging exception catcher --- .../api/include/ginvoke.hpp | 130 ++++++++++++++++++ .../api/include/hexify.hpp | 12 ++ .../api/include/make_array.hpp | 15 ++ .../api/include/overload.hpp | 10 ++ .../windows-notification/api/src/ginvoke.cpp | 35 +++++ 5 files changed, 202 insertions(+) create mode 100644 plugins/windows-notification/api/include/ginvoke.hpp create mode 100644 plugins/windows-notification/api/include/hexify.hpp create mode 100644 plugins/windows-notification/api/include/make_array.hpp create mode 100644 plugins/windows-notification/api/include/overload.hpp create mode 100644 plugins/windows-notification/api/src/ginvoke.cpp diff --git a/plugins/windows-notification/api/include/ginvoke.hpp b/plugins/windows-notification/api/include/ginvoke.hpp new file mode 100644 index 00000000..5d4b766d --- /dev/null +++ b/plugins/windows-notification/api/include/ginvoke.hpp @@ -0,0 +1,130 @@ + +#ifndef GINVOKE_HPP +#define GINVOKE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; + 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(*this)); + } + }; + + struct hresult + { + std::int32_t code; + varstring message; + }; + std::optional get_if_hresult_error(std::exception_ptr) noexcept; +} + +template +inline auto &describe_argument(OStream &s, const T &a) { return s << a; } +template +inline auto &describe_argument(OStream &s, std::string_view const a) { return s << std::quoted(a); } +template +inline auto &describe_argument(OStream &s, const std::string & a) { return s << std::quoted(a); } +template +inline auto &describe_argument(OStream &s, const char * const a) { return s << std::quoted(a); } +// TODO: overload for const GString * +template +inline auto &describe_argument(OStream &s, std::wstring_view const a) { return s << std::quoted(a); } +template +inline auto &describe_argument(OStream &s, const std::wstring & a) { return s << std::quoted(a); } +template +inline auto &describe_argument(OStream &s, const wchar_t * const a) = delete; // not implemented +// TODO: handle wide strings maybe + +template +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 {""}; +} + + +#define FORMAT "%s(%s) failed: %s" +template +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 +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 +inline gboolean try_invoke(const char *func_name, Invokable &&i, const Arg &...a) noexcept try +{ + return std::invoke(std::forward(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(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 diff --git a/plugins/windows-notification/api/include/hexify.hpp b/plugins/windows-notification/api/include/hexify.hpp new file mode 100644 index 00000000..e1c90a0d --- /dev/null +++ b/plugins/windows-notification/api/include/hexify.hpp @@ -0,0 +1,12 @@ + +#ifndef HEXIFY_HPP +#define HEXIFY_HPP +#include + +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 diff --git a/plugins/windows-notification/api/include/make_array.hpp b/plugins/windows-notification/api/include/make_array.hpp new file mode 100644 index 00000000..ff4a9dc7 --- /dev/null +++ b/plugins/windows-notification/api/include/make_array.hpp @@ -0,0 +1,15 @@ + +#ifndef MAKE_ARRAY_HPP +#define MAKE_ARRAY_HPP +#include +#include + +template +inline auto make_array(const char (&from_literal)[N]) noexcept +{ + static_assert( N ); + std::array a; + std::copy(+from_literal, from_literal+a.size(), a.begin()); + return a; +} +#endif diff --git a/plugins/windows-notification/api/include/overload.hpp b/plugins/windows-notification/api/include/overload.hpp new file mode 100644 index 00000000..c0da3cf6 --- /dev/null +++ b/plugins/windows-notification/api/include/overload.hpp @@ -0,0 +1,10 @@ + +#ifndef OVERLOAD_HPP +#define OVERLOAD_HPP +template +struct overload : Callable... +{ + overload( Callable &&...c ) : Callable{std::move(c)}... {} + using Callable::operator()...; +}; +#endif diff --git a/plugins/windows-notification/api/src/ginvoke.cpp b/plugins/windows-notification/api/src/ginvoke.cpp new file mode 100644 index 00000000..b34e76fe --- /dev/null +++ b/plugins/windows-notification/api/src/ginvoke.cpp @@ -0,0 +1,35 @@ + +#include "ginvoke.hpp" +#include "converter.hpp" + +#include + + +namespace glib::impl +{ + std::optional 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(ptr)); // WTF? Deletion is not modification! + return {{ e.code(), {std::move(msg)} }}; + } + catch (...) + { + g_free(const_cast(ptr)); + return {{ e.code(), {""} }}; + } + } + catch (...) + { + // This is not the exception you are looking for. + return {}; + } +}