Last active
October 27, 2022 07:31
-
-
Save kice/cd2c254309349c7129edb38cded0a909 to your computer and use it in GitHub Desktop.
<fmt> logging library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#ifdef _DEBUG | |
#define FMTLOG_ENABLE | |
#endif | |
#include <string> | |
#include <functional> | |
#define FMT_HEADER_ONLY | |
#include "fmt/format.h" | |
#include "fmt/xchar.h" | |
#define LOG_LEVEL_TRACE 0 | |
#define LOG_LEVEL_DEBUG 1 | |
#define LOG_LEVEL_INFO 2 | |
#define LOG_LEVEL_WARN 3 | |
#define LOG_LEVEL_ERROR 4 | |
#define LOG_LEVEL_CRITICAL 5 | |
#define LOG_LEVEL_OFF 6 | |
#define LOG_LEVEL_ALWAYS 7 | |
namespace fmtlog | |
{ | |
#ifdef FMTLOG_NO_CALLBACK | |
template<typename ...Args> | |
constexpr auto filter(Args&&... t) -> bool { return true; } | |
template<typename ...Args> | |
constexpr auto callback(Args&&... t) {} | |
#else | |
bool filter(int level, const std::string_view category); | |
void callback(int level, const std::string_view category, const char *location, const std::string_view message); | |
void callback(int level, const std::string_view category, const wchar_t *location, const std::wstring_view message); | |
#endif // FMTLOG_NO_CALLBACK | |
#ifdef FMTLOG_CALLBACK_STDOUT | |
inline bool filter(int level, const std::string_view category) | |
{ | |
return true; | |
} | |
inline void callback(int level, const std::string_view category, const char *location, const std::string_view message) | |
{ | |
fprintf(stdout, "%.*s\n", message.size(), message.data()); | |
} | |
inline void callback(int level, const std::string_view category, const wchar_t *location, const std::wstring_view message) | |
{ | |
// expect to be ???? for non-ascii char on cmd.exe | |
fwprintf(stdout, L"%.*s\n", message.size(), message.data()); | |
} | |
#endif | |
}; | |
namespace fmtlog | |
{ | |
enum loglevel | |
{ | |
trace = LOG_LEVEL_TRACE, | |
debug = LOG_LEVEL_DEBUG, | |
info = LOG_LEVEL_INFO, | |
warn = LOG_LEVEL_WARN, | |
error = LOG_LEVEL_ERROR, | |
critical = LOG_LEVEL_CRITICAL, | |
off = LOG_LEVEL_OFF, | |
always = LOG_LEVEL_ALWAYS, | |
n_levels, | |
}; | |
namespace details | |
{ | |
#ifdef _WIN32 | |
#include <Windows.h> | |
#define CP_SHIFTJIS 932 | |
#define CP_GBK 936 | |
#define CP_BIG5 950 | |
#define CP_UTF8 65001 | |
#define CP_UTF16 1200 | |
inline int _wtoa(const wchar_t *w, char *a, int chars, UINT codepage = CP_THREAD_ACP) | |
{ | |
return WideCharToMultiByte(codepage, 0, w, -1, a, (int)(a ? chars : 0), 0, 0); | |
} | |
inline int _atow(const char *a, wchar_t *w, int chars, UINT codepage = CP_THREAD_ACP) | |
{ | |
return MultiByteToWideChar(codepage, 0, a, -1, w, (int)(w ? chars : 0)); | |
} | |
#else | |
#define HIGH_SURROGATE_START 0xd800 | |
#define HIGH_SURROGATE_END 0xdbff | |
#define LOW_SURROGATE_START 0xdc00 | |
#define LOW_SURROGATE_END 0xdfff | |
#define IS_HIGH_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && ((wch) <= HIGH_SURROGATE_END)) | |
#define IS_LOW_SURROGATE(wch) (((wch) >= LOW_SURROGATE_START) && ((wch) <= LOW_SURROGATE_END)) | |
#define IS_SURROGATE_PAIR(hs, ls) (IS_HIGH_SURROGATE(hs) && IS_LOW_SURROGATE(ls)) | |
static inline int _wtoa(const wchar_t *w, char *a, int chars, UINT codepage = 0) | |
{ | |
return wcstombs(a, w, chars - 1) + 1; | |
} | |
static inline int _atow(const char *a, wchar_t *w, int chars, UINT codepage = 0) | |
{ | |
return mbstowcs(w, a, chars - 1) + 1; | |
} | |
#endif // _WIN32 | |
// convert ANSI to utf-16le | |
inline std::wstring atow(const std::string_view &string) | |
{ | |
int len = _atow(string.data(), 0, 0); | |
wchar_t *buffer = new wchar_t[len]; | |
memset(buffer, 0, len * sizeof(*buffer)); | |
_atow(string.data(), buffer, (int)len); | |
std::wstring s = buffer; | |
delete[] buffer; | |
return s; | |
} | |
// on windows, wchar_t should be stored as UTF-16LE | |
inline std::wstring u8tow(const std::string_view &string) | |
{ | |
int len = _atow(string.data(), 0, 0, CP_UTF8); | |
wchar_t *buffer = new wchar_t[len]; | |
memset(buffer, 0, len * sizeof(*buffer)); | |
_atow(string.data(), buffer, (int)len, CP_UTF8); | |
std::wstring s = buffer; | |
delete[] buffer; | |
return s; | |
} | |
// convert utf-16le to ANSI | |
inline std::string wtoa(const std::wstring_view &string) | |
{ | |
int len = _wtoa(string.data(), 0, 0); | |
char *buffer = new char[len]; | |
memset(buffer, 0, len * sizeof(*buffer)); | |
_wtoa(string.data(), buffer, (int)len); | |
std::string s = buffer; | |
delete[] buffer; | |
return s; | |
} | |
// convert utf-16le to utf-8 | |
inline std::string wtou8(const std::wstring_view &string) | |
{ | |
int len = _wtoa(string.data(), 0, 0, CP_UTF8); | |
char *buffer = new char[len]; | |
memset(buffer, 0, len * sizeof(*buffer)); | |
_wtoa(string.data(), buffer, (int)len, CP_UTF8); | |
std::string s = buffer; | |
delete[] buffer; | |
return s; | |
} | |
#if __cpp_nontype_template_args >= 201911L and !defined(FMTLOG_FULLPATH) | |
#define __FMTLOG_USE_FILENAME_LITERIAL | |
template<typename _Elem, size_t N> | |
struct FileName | |
{ | |
_Elem fname[N]{ 0 }; | |
constexpr FileName(const _Elem(&fp)[N]) | |
{ | |
#if _WIN32 | |
constexpr auto path_sep = (_Elem)'\\'; | |
#else | |
constexpr auto path_sep = (_Elem)'/'; | |
#endif | |
_Elem rfname[N]{ 0 }; | |
auto pos = 0; | |
for (int i = N - 1; i >= 0; --i) { | |
if (fp[i] == '"') { | |
continue; | |
} | |
if (fp[i] == path_sep) { | |
break; | |
} | |
rfname[pos++] = fp[i]; | |
} | |
for (int i = 0; pos > 0; ++i) { | |
fname[i] = rfname[--pos]; | |
} | |
} | |
}; | |
#endif | |
} | |
namespace string_type | |
{ | |
template <typename T> struct is_char : std::false_type {}; | |
template <> struct is_char<char> : std::true_type {}; | |
template <> struct is_char<wchar_t> : std::true_type {}; | |
#if __cpp_lib_char8_t >= 201907L | |
template <> struct is_char<char8_t> : std::true_type {}; | |
#endif | |
template <> struct is_char<char16_t> : std::true_type {}; | |
template <> struct is_char<char32_t> : std::true_type {}; | |
template <typename Char, std::enable_if_t<(is_char<Char>::value), int> = 0> | |
inline auto to_string_view(const Char *s) | |
-> std::basic_string_view<Char> | |
{ | |
return s; | |
} | |
template <typename Char, typename Traits, typename Alloc> | |
inline auto to_string_view(const std::basic_string<Char, Traits, Alloc> &s) | |
-> std::basic_string_view<Char> | |
{ | |
return s; | |
} | |
template <typename Char> | |
constexpr auto to_string_view(std::basic_string_view<Char> s) | |
-> std::basic_string_view<Char> | |
{ | |
return s; | |
} | |
#if defined(FMT_VERSION) | |
template <typename S, std::enable_if_t<(fmt::is_compile_string<S>::value), int> = 0> | |
constexpr auto to_string_view(const S &s) | |
-> std::basic_string_view<typename S::char_type> | |
{ | |
return {}; | |
} | |
#endif | |
void to_string_view(...); | |
template<typename char_type> | |
struct string_mapper | |
{ | |
using string_view_t = std::basic_string_view<char_type>; | |
constexpr inline auto map(char_type *val) -> const char_type * | |
{ | |
return val; | |
} | |
constexpr inline auto map(const char_type *val) -> const char_type * | |
{ | |
return val; | |
} | |
template <typename S> | |
struct _is_string | |
: std::is_same<decltype(to_string_view(std::declval<const S &>())), string_view_t> | |
{ | |
}; | |
template <typename T, std::enable_if_t<(_is_string<T>::value), int> = 0> | |
constexpr inline auto map(const T &val)-> string_view_t | |
{ | |
return to_string_view(val); | |
} | |
struct not_string {}; | |
auto map(...) -> not_string { return {}; } | |
}; | |
// | |
//template<typename T, typename Char> | |
//using test_type = decltype(string_mapper<Char>().map(std::declval<const T &>())); | |
// | |
//using a_type = test_type<std::string, char>; | |
//static_assert(std::is_same_v<a_type, std::string_view>); | |
template <typename S, class char_type, class string_type> | |
struct is_string_t | |
: std::is_same<decltype(string_mapper<char_type>().map(std::declval<const S &>())), | |
string_type> | |
{ | |
}; | |
template <typename S> | |
struct is_cwstring : is_string_t<S, wchar_t, const wchar_t *> {}; | |
template <typename S> | |
struct is_wstring : is_string_t<S, wchar_t, std::wstring_view> {}; | |
template <typename S> | |
struct is_cstring : is_string_t<S, char, const char *> {}; | |
template <typename S> | |
struct is_string : is_string_t<S, char, std::string_view> {}; | |
} | |
template<class F> | |
struct category_logger | |
{ | |
F f; | |
bool invoke = true; | |
explicit category_logger(F &&_f) noexcept : f(_f) | |
{ | |
} | |
category_logger(category_logger &&other) noexcept | |
: f(std::move(other.f)), invoke(std::exchange(other.invoke, false)) | |
{ | |
} | |
category_logger(const category_logger &) = delete; | |
category_logger &operator=(const category_logger &) = delete; | |
category_logger &operator=(category_logger &&) = delete; | |
~category_logger() | |
{ | |
if (invoke) { | |
f(std::string_view()); | |
} | |
} | |
template<typename T> | |
void operator()(const T &category) | |
{ | |
if (invoke) { | |
f(category); | |
} | |
invoke = false; | |
} | |
}; | |
struct empty_logger | |
{ | |
template<typename T> | |
constexpr void operator()(T &&) const noexcept | |
{ | |
} | |
}; | |
template<typename _Char> | |
inline void vlog(int level, const std::string_view category, | |
const _Char *location, const _Char *func, | |
const fmt::basic_string_view<_Char> format, bool func_tag, | |
fmt::basic_format_args<fmt::buffer_context<_Char>> args) | |
{ | |
fmt::basic_memory_buffer<_Char> out; | |
if constexpr (std::is_same_v<_Char, wchar_t>) { | |
if (func_tag) { | |
fmt::format_to(std::back_inserter(out), FMT_STRING(L"{} [{}]: "), location, func); | |
} else { | |
fmt::format_to(std::back_inserter(out), FMT_STRING(L"{}: "), location); | |
} | |
} else { | |
if (func_tag) { | |
fmt::format_to(std::back_inserter(out), FMT_STRING("{} [{}]: "), location, func); | |
} else { | |
fmt::format_to(std::back_inserter(out), FMT_STRING("{}: "), location); | |
} | |
} | |
fmt::detail::vformat_to(out, format, args); | |
callback(level, category, location, fmt::to_string(out)); | |
} | |
#if __cpp_nontype_template_args >= 201911L | |
template<typename _Elem, size_t N> | |
struct VariableName | |
{ | |
_Elem str[N]{ 0 }; | |
constexpr VariableName(const _Elem(&name)[N]) | |
{ | |
for (int i = 0; i < N; ++i) { | |
str[i] = name[i]; | |
} | |
} | |
}; | |
template<typename T, VariableName name, VariableName wname> | |
struct VariableRepr | |
{ | |
const T &ref; | |
VariableRepr(const T &var) : ref(var) {} | |
}; | |
#else | |
template<typename T> | |
struct VariableRepr | |
{ | |
const T &ref; | |
std::string_view name; | |
std::wstring_view wname; | |
VariableRepr(const T &var, const std::string_view str, const std::wstring_view wstr) | |
: ref(var), name(str), wname(wstr) | |
{ | |
} | |
}; | |
#endif | |
#if __cpp_generic_lambdas >= 201707L | |
template <typename S> | |
inline auto log(int level, const char *loc, const wchar_t *wloc, | |
const char *func, const wchar_t *wfunc, | |
const S &format, bool func_tag, auto&&... args) | |
{ | |
using namespace string_type; | |
auto f = [level, loc, wloc, func, wfunc, func_tag, format = std::move(format)] | |
<typename ...Args> | |
(const std::string_view category, Args&& ...args) | |
{ | |
if (!filter(level, category)) { | |
return; | |
} | |
auto to_str = [](auto x) { | |
if constexpr (is_wstring<decltype(x)>::value || is_cwstring<decltype(x)>::value) { | |
return details::wtoa(x); | |
} else { | |
return x; | |
} | |
}; | |
auto to_wstr = [](auto x) { | |
if constexpr (is_string<decltype(x)>::value || is_cstring<decltype(x)>::value) { | |
return details::atow(x); | |
} else { | |
return x; | |
} | |
}; | |
static_assert(fmt::is_compile_string<S>::value); | |
using format_char_type = typename S::char_type; | |
if constexpr (std::is_same<format_char_type, wchar_t>::value) { | |
vlog<wchar_t>(level, category, wloc, wfunc, format, func_tag, | |
fmt::make_args_checked<decltype(to_wstr(args))...>(format, to_wstr(args)...)); | |
} else { | |
vlog<char>(level, category, loc, func, format, func_tag, | |
fmt::make_args_checked<decltype(to_str(args))...>(format, to_str(args)...)); | |
} | |
}; | |
return category_logger(std::bind(f, std::placeholders::_1, std::forward<decltype(args)>(args)...)); | |
} | |
#else | |
template <typename S, typename ...Args> | |
inline auto log(int level, const char *loc, const wchar_t *wloc, | |
const char *func, const wchar_t *wfunc, | |
const S &format, bool func_tag, auto&&... args) | |
{ | |
using namespace string_type; | |
using namespace details; | |
auto to_str = [](auto x) { | |
if constexpr (is_wstring<decltype(x)>::value || is_cwstring<decltype(x)>::value) { | |
return wtoa(x); | |
} else { | |
return x; | |
} | |
}; | |
auto to_wstr = [](auto x) { | |
if constexpr (is_string<decltype(x)>::value || is_cstring<decltype(x)>::value) { | |
return atow(x); | |
} else { | |
return x; | |
} | |
}; | |
static_assert(fmt::is_compile_string<S>::value); | |
using format_char_type = typename S::char_type; | |
if constexpr (std::is_same<format_char_type, wchar_t>::value) { | |
vlog<wchar_t>(level, "", wloc, wfunc, format, func_tag, | |
fmt::make_args_checked<decltype(to_wstr(args))...>(format, to_wstr(args)...)); | |
} else { | |
vlog<char>(level, "", loc, func, format, func_tag, | |
fmt::make_args_checked<decltype(to_str(args))...>(format, to_str(args)...)); | |
} | |
return [](auto &&) {}; | |
} | |
#endif | |
namespace formatter | |
{ | |
struct atow_formatter : fmt::formatter<std::wstring_view, wchar_t> | |
{ | |
template<typename FormatContext> | |
auto format(std::string_view const &str, FormatContext &ctx) | |
{ | |
using namespace fmtlog::details; | |
return fmt::formatter<std::wstring_view, wchar_t>::format(atow(str), ctx); | |
} | |
}; | |
struct wtoa_formatter : fmt::formatter<std::string_view, char> | |
{ | |
template<typename FormatContext> | |
auto format(std::wstring_view const &str, FormatContext &ctx) | |
{ | |
using namespace fmtlog::details; | |
return fmt::formatter<std::string_view, char>::format(wtoa(str), ctx); | |
} | |
}; | |
template<typename Char> | |
struct formatter_map | |
{ | |
#if _HAS_CXX20 | |
template<typename T> | |
using remove_cvref_t = std::remove_cvref_t<T>; | |
#else | |
template<typename T> | |
using remove_cvref_t = typename std::remove_cv<std::remove_reference_t<T>>::type; | |
#endif | |
using args_char = std::conditional_t<std::is_same_v<Char, char>, wchar_t, char>; | |
using string_view_t = std::basic_string_view<args_char>; | |
template <typename S> | |
struct _is_string | |
: std::is_same<decltype(fmtlog::string_type::to_string_view(std::declval<const S &>())), string_view_t> | |
{ | |
}; | |
template <typename T, std::enable_if_t<(_is_string<T>::value), int> = 0> | |
constexpr inline auto map(const T &val) | |
{ | |
if constexpr (std::is_same_v<args_char, char>) { | |
return atow_formatter{}; | |
} else { | |
return wtoa_formatter{}; | |
} | |
} | |
template<typename T> | |
auto map(T &&) -> fmt::formatter<remove_cvref_t<T>, Char> { return {}; } | |
}; | |
template<typename Arg, typename Target> | |
using mapper = decltype(formatter_map<Target>().map(std::declval<const Arg &>())); | |
} | |
} | |
#define __TOWIDE2(x) L##x | |
#define __TOWIDE(x) __TOWIDE2(x) | |
#define __FMTLOG_TOWIDE2(x) L##x | |
#define __FMTLOG_TOWIDE(x) __FMTLOG_TOWIDE2(x) | |
#define __FMTLOG_WS1(x) L""#x | |
#define __FMTLOG_WS2(x) __FMTLOG_WS1(x) | |
#define __FMTLOG_S1(x) #x | |
#define __FMTLOG_S2(x) __FMTLOG_S1(x) | |
#define __FMTLOG_I(x) x | |
#if __cpp_nontype_template_args >= 201911L | |
template<typename T, typename Char, fmtlog::VariableName name, fmtlog::VariableName wname> | |
struct fmt::formatter<fmtlog::VariableRepr<T, name, wname>, Char> : fmtlog::formatter::mapper<T, Char> | |
{ | |
template<typename FormatContext> | |
auto format(fmtlog::VariableRepr<T, name, wname> const &repr, FormatContext &ctx) | |
{ | |
if constexpr (std::is_same_v<Char, char>) { | |
// format_to(ctx.out(), "{}=", name.str); | |
auto appender = ctx.out(); | |
// appender = name.str; | |
appender = '='; | |
} else { | |
format_to(ctx.out(), L"{}=", wname.str); | |
} | |
return fmtlog::formatter::mapper<T, Char>::format(repr.ref, ctx); | |
} | |
}; | |
#define F(var) fmtlog::VariableRepr<decltype(var), __FMTLOG_S2(var), __FMTLOG_WS2(var)>(var) | |
#else | |
template<typename T, typename Char> | |
struct fmt::formatter<fmtlog::VariableRepr<T>, Char> : fmtlog::formatter::mapper<T, Char> | |
{ | |
template<typename FormatContext> | |
auto format(fmtlog::VariableRepr<T> const &repr, FormatContext &ctx) | |
{ | |
using namespace fmtlog::string_type; | |
if constexpr (std::is_same<Char, char>::value) { | |
format_to(ctx.out(), "{}=", repr.name); | |
} else { | |
format_to(ctx.out(), L"{}=", repr.wname); | |
} | |
return fmtlog::formatter::mapper<T, Char>::format(repr.ref, ctx); | |
} | |
}; | |
#define F(var) fmtlog::VariableRepr<decltype(var)>(var, __FMTLOG_S2(var), __FMTLOG_WS2(var)) | |
#endif | |
#ifdef __FMTLOG_USE_FILENAME_LITERIAL | |
template<fmtlog::details::FileName F> | |
constexpr auto operator"" _fmtlog_fname() { return F.fname; } | |
#else | |
constexpr auto operator"" _fmtlog_fname(const char *str, std::size_t len) { return str; } | |
constexpr auto operator"" _fmtlog_fname(const wchar_t *str, std::size_t len) { return str; } | |
#endif | |
#define __FILE_NAME__ __FMTLOG_I(__FILE__)_fmtlog_fname | |
#define __FILE_NAMEW__ __FMTLOG_TOWIDE(__FILE__)_fmtlog_fname | |
#define __FMTLOG_LOCATION __FILE_NAME__ "(" __FMTLOG_S2(__LINE__) ")" | |
#define __FMTLOG_LOCATION_W __FILE_NAMEW__ L"(" __FMTLOG_WS2(__LINE__) L")" | |
#define __FMTLOG_FUNC __func__ | |
#define __FMTLOG_FUNC_W __FMTLOG_TOWIDE(__FUNCTION__) | |
#define logn(format, ...) \ | |
fmtlog::log(fmtlog::loglevel::always, \ | |
__FMTLOG_LOCATION, __FMTLOG_LOCATION_W, \ | |
__FMTLOG_FUNC, __FMTLOG_FUNC_W, \ | |
FMT_STRING(format), false, __VA_ARGS__) | |
#define lognf(format, ...) \ | |
fmtlog::log(fmtlog::loglevel::always, \ | |
__FMTLOG_LOCATION, __FMTLOG_LOCATION_W, \ | |
__FMTLOG_FUNC, __FMTLOG_FUNC_W, \ | |
FMT_STRING(format), true, __VA_ARGS__) | |
#ifdef FMTLOG_ENABLE | |
#define LOG_IMPL(level, format, func_tag, ...) \ | |
fmtlog::log(level, \ | |
__FMTLOG_LOCATION, __FMTLOG_LOCATION_W, \ | |
__FMTLOG_FUNC, __FMTLOG_FUNC_W, \ | |
FMT_STRING(format), func_tag, __VA_ARGS__) | |
#define logt(format, ...) LOG_IMPL(fmtlog::loglevel::trace, format, false, __VA_ARGS__) | |
#define logd(format, ...) LOG_IMPL(fmtlog::loglevel::debug, format, false, __VA_ARGS__) | |
#define logi(format, ...) LOG_IMPL(fmtlog::loglevel::info, format, false, __VA_ARGS__) | |
#define logw(format, ...) LOG_IMPL(fmtlog::loglevel::warn, format, false, __VA_ARGS__) | |
#define loge(format, ...) LOG_IMPL(fmtlog::loglevel::error, format, false, __VA_ARGS__) | |
#define logc(format, ...) LOG_IMPL(fmtlog::loglevel::critical, format, false, __VA_ARGS__) | |
#define logf(format, ...) LOG_IMPL(fmtlog::loglevel::debug, format, true, __VA_ARGS__) | |
#define logfi(format, ...) LOG_IMPL(fmtlog::loglevel::info, format, true, __VA_ARGS__) | |
#define logfw(format, ...) LOG_IMPL(fmtlog::loglevel::warn, format, true, __VA_ARGS__) | |
#define logfe(format, ...) LOG_IMPL(fmtlog::loglevel::error, format, true, __VA_ARGS__) | |
#else | |
#define LOG_IMPL(...) fmtlog::empty_logger() | |
#define logt(format, ...) LOG_IMPL() | |
#define logd(format, ...) LOG_IMPL() | |
#define logi(format, ...) LOG_IMPL() | |
#define logw(format, ...) LOG_IMPL() | |
#define loge(format, ...) LOG_IMPL() | |
#define logc(format, ...) LOG_IMPL() | |
#define logf(format, ...) LOG_IMPL() | |
#define logfi(format, ...) LOG_IMPL() | |
#define logfw(format, ...) LOG_IMPL() | |
#define logfe(format, ...) LOG_IMPL() | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define FMTLOG_CALLBACK_STDOUT | |
#include "fmtlog.h" | |
auto main() -> int | |
{ | |
auto wstr = std::wstring(L"自动转换-abc"); | |
auto str = std::string("自动转换-ABC"); | |
// Need to set console to UTF-8 output mode to show non-ascii characters for using wcout/wprintf | |
int abc = 123; | |
logd("{} {} {}", F(abc), F(wstr), F(str)); | |
// main.cpp(12): abc=123 wstr=自动转换-abc str=自动转换-ABC | |
logd(L"{} {} {}", F(abc), F(wstr), F(str)); | |
// main.cpp(14): abc=123 wstr=????-abc str=????-ABC | |
auto sv = std::string_view("中文"); | |
auto wsv = std::wstring_view(L"中文"); | |
auto cstr = "测试"; | |
auto wcstr = "测试"; | |
logd("{} {} {} {}", F(sv), F(wsv), F(cstr), F(wcstr)); | |
// main.cpp(23): sv=中文 wsv=中文 cstr=测试 wcstr=测试 | |
logd(L"{} {} {} {}", F(sv), F(wsv), F(cstr), F(wcstr)); | |
// main.cpp(25): sv=?? wsv=?? cstr=?? wcstr=?? | |
logt("invalid squishiness: {}{}{}", std::string_view("中文"), "测试", str)("test"); | |
// main.cpp(28): invalid squishiness: 中文测试自动转换-ABC | |
logd(L"invalid squishiness: {}{}{}", std::wstring_view(L"中文"), L"测试", wstr)(std::string("test")); | |
// main.cpp(31): invalid squishiness: ????????-abc | |
logi(L"invalid squishiness: {}{}{}", std::string_view("中文"), "测试", str)(std::string_view("test")); | |
// main.cpp(33): invalid squishiness: ????????-ABC | |
logw("invalid squishiness: {}{}{}", std::wstring_view(L"中文"), L"测试", wstr); | |
// main.cpp(36): invalid squishiness: 中文测试自动转换-abc | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment