#ifndef GINVOKE_HPP #define GINVOKE_HPP #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 { varstring(std::string &&s) noexcept : varstring_t{std::move(s)} {} varstring(static_c_str &&s) noexcept : varstring_t{std::move(s)} {} varstring(std::nullptr_t) = delete; varstring(const varstring &) = delete; varstring(varstring &&) = default; 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,int> = 0> inline auto &describe_argument(OStream &s, const T &a) { return s << a; } template,int> = 0> inline auto &describe_argument(OStream &s, const T &a) { return s << static_cast>(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 * // not implemented (TODO maybe): template inline auto &describe_argument(OStream &s, std::wstring_view const a) = delete; template inline auto &describe_argument(OStream &s, const std::wstring & a) = delete; template inline auto &describe_argument(OStream &s, const wchar_t * const a) = delete; inline impl::varstring describe_arguments() noexcept { return {""}; } 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 {std::move(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 struct regular_void {}; template inline auto invoke(Invokable &&i, const Arg &... a) { using R = decltype(std::invoke(std::forward(i), a...)); if constexpr (std::is_void_v) { std::invoke(std::forward(i), a...); return regular_void{}; } else return std::invoke(std::forward(i), a...); } template inline auto try_invoke( const char *func_name, Invokable &&i, const Arg &... a) noexcept -> std::optional(i), a...))> try { return invoke(std::forward(i), a...); } catch (const std::exception &e) { log_invocation_failure(e.what(), func_name, a...); return {}; } 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 {}; } } // namespace glib #define g_try_invoke(invokable, ...) \ glib::try_invoke(#invokable, invokable, __VA_ARGS__) #define g_try_invoke0(invokable) \ glib::try_invoke(#invokable, invokable) #endif