【发布时间】:2021-12-30 20:22:37
【问题描述】:
我正在为一个个人项目开发一个事件系统,我正在尝试让事件像LOG(event) 一样简单地记录到控制台。
在这种情况下,事件由 Event 类定义,该类具有一些方法和一个 virtual ToString() 函数,该函数返回一个字符串,其中包含事件信息以及我希望在每个事件上输出的任何内容。通过定义继承自 Event 类的特定事件类并根据每个事件的作用和它具有的变量定义 ToString() 函数,进一步扩展了此类。
所以我的LOG 宏调用了一个静态 Log 类的函数,通过将参数转换为字符串来在控制台中显示消息,如下所示:
#define LOG(...) Log::DisplayLog(LogUtils::StringFromArgs(__VA_ARGS__))
这样做是因为DisplayLog() 还接收其他对我的问题不重要的信息参数。
LogUtils::StringFromArgs() 函数通过执行以下操作使用 fmt 将参数转换为字符串:
template<typename FormatString, typename... Args>
inline std::string StringFromArgs(const FormatString& fmt, const Args &... args)
{
char arg_string[1024];
memset(arg_string, 0, sizeof(arg_string));
fmt::format_to(arg_string, fmt, args...);
return std::string(arg_string);
}
所以,当我为此使用 fmt 时,我认为按照 fmt 指南创建事件日志会很容易,我的想法是为事件类:
template<typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<Event, T>::value, char>> : fmt::formatter<std::string>
{
template<typename ParseContext>
constexpr auto parse(ParseContext& ctx) { return ctx.begin(); }
template<typename FormatCtx>
auto format(const T& event, FormatCtx& ctx) // also tried Event& instead of T&
{
return fmt::format_to(ctx.out(), "{0}", event.ToString());
// also tried:
//return fmt::formatter<std::string>::format(event.ToString(), ctx);
}
};
但是,这不起作用,当我尝试执行特定事件类的 LOG(event)(假设“事件”是 WindowResizedEvent 继承自 Event 类)时,我一直从 fmt 遇到相同的错误,因为它无法格式化事件(我也尝试在 Event 类中添加 const char*() 运算符,但仍然相同):
“论点 2”是“事件”论点(正如我所说,我中间还有一些其他论点对这个问题并不重要)。
有谁知道如何在不为每种类型的事件类指定格式化程序的情况下对其进行格式化?因为每种事件类型的代码总是相同的。
【问题讨论】:
-
LOG(event): 你的宏/函数需要一个格式字符串作为第一个参数。 -
不,问题出在从 StringFromArgs() 调用的 fmt::format_to() 函数上,不在宏中,宏不需要变量类型
-
fmt::format_to的第二个参数必须可转换为格式字符串。您呈现代码的方式,LOG只是将其第一个参数传递给format_to的第二个参数。Event看起来不是一个有效的格式字符串或不能转换为一个。 -
嗯,是的,但关键是如何做到这一点(如下回答)
-
fmt的全部意义在于您可以编写格式字符串,例如"Error at line {} in object {}."并将{}替换为对传递给format_to函数的某些对象进行编码的字符串。如果您不需要这样的格式字符串,那么您只需要将运算符转换为std::string。