#pragma once #include #include #include #include "unicode.h" namespace logging { enum class Level : int { Verbose = 0, Debug = 1, Info = 2, Warning = 3, Error = 4, Fatal = 5, }; enum FastFailErrorCode : int { Unspecified = 12345, MinHookUnload, }; /** * @brief Starts writing log to specified file. */ void start_file_logging(const std::filesystem::path& logPath, bool redirect_stderrout = false); /** * @brief Marks this DLL either as loaded or unloaded, top prevent accessing handles when the DLL is not loaded. */ void update_dll_load_status(bool loaded); /** * @brief Prints log, unformatted. * @param level Log level. * @param s Log to print, as a C-string. */ template void print(Level level, const TElem* s) { print(level, unicode::convert(s).c_str()); } /** * @brief Prints log, unformatted. * @param level Log level. * @param s Log to print, as a basic_string. */ template, typename TAlloc = std::allocator> void print(Level level, const std::basic_string& s) { print(level, s.c_str()); } /** * @brief Prints log, unformatted. * @param level Log level. * @param s Log to print, as a basic_string_view. */ template> void print(Level level, const std::basic_string_view& s) { print(level, unicode::convert(s).c_str()); } template<> void print(Level level, const char* s); template struct is_basic_string : std::false_type {}; template struct is_basic_string> : std::true_type {}; template inline constexpr auto is_basic_string_v = is_basic_string::value; template struct is_basic_string_view : std::false_type {}; template struct is_basic_string_view> : std::true_type {}; template inline constexpr auto is_basic_string_view_v = is_basic_string_view::value; template auto to_format_arg(T&& x) { using Td = std::remove_cvref_t; if constexpr (std::is_pointer_v) { using Tdd = std::remove_cvref_t>; if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) return unicode::convert(x); else return std::forward(x); } else { if constexpr (is_basic_string_v || is_basic_string_view_v) { using Tdd = Td::value_type; if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) return unicode::convert(x); else return std::forward(x); } else if constexpr (std::is_same_v) { auto u8s = x.u8string(); return std::move(*reinterpret_cast(&u8s)); } else { return std::forward(x); } } } /** * @brief Prints log, formatted. * @param level Log level. * @param fmt C-string. * @param arg1 First format parameter. * @param args Second and further format parameters, if any. */ template void print(Level level, const char* fmt, Arg&& arg1, Args&&... args) { // make_format_args only accepts references now due to https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2905r2.html // we can switch std::runtime_format in C++26 :) https://isocpp.org/files/papers/P2918R0.html auto transformed_args = std::make_tuple(to_format_arg(arg1), to_format_arg(args)...); auto format_args = std::apply( [&](auto&... elems) { return std::make_format_args(elems...); }, transformed_args ); print(level, std::vformat(fmt, format_args)); } template void V(Args&&...args) { print(Level::Verbose, std::forward(args)...); } template void D(Args&&...args) { print(Level::Debug, std::forward(args)...); } template void I(Args&&...args) { print(Level::Info, std::forward(args)...); } template void W(Args&&...args) { print(Level::Warning, std::forward(args)...); } template void E(Args&&...args) { print(Level::Error, std::forward(args)...); } template void F(Args&&...args) { print(Level::Fatal, std::forward(args)...); } }