我正在为个人项目开发一个事件系统,我正在尝试将事件记录到控制台就像
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 的相同错误,因为它可以' t 格式化事件(我也尝试在 const char*()
类中添加 Event
运算符,但仍然相同):
“参数 2”是“事件”参数(正如我所说,中间还有一些对这个问题并不重要的其他参数)。
有谁知道如何在不为每种类型的事件类指定格式化程序的情况下对其进行格式化?因为每种事件类型的代码始终相同。
格式字符串应作为
fmt::format_string
传递,而不是作为模板参数传递。这是一个工作示例(https://godbolt.org/z/1Pf75Y84K):
#include <fmt/format.h>
struct Event {
virtual std::string ToString() const = 0;
};
struct MyEvent : Event {
std::string ToString() const override {
return "foo";
}
};
template<typename T>
struct fmt::formatter<
T, std::enable_if_t<std::is_base_of<Event, T>::value, char>>
: fmt::formatter<std::string> {
auto format(const T& event, fmt::format_context& ctx) const {
return fmt::format_to(ctx.out(), "{}", event.ToString());
}
};
#define LOG(...) fmt::print("{}", StringFromArgs(__VA_ARGS__))
template <typename... T>
std::string StringFromArgs(fmt::format_string<T...> fmt, T&&... args) {
return fmt::format(fmt, std::forward<T>(args)...);
}
int main() {
LOG("{}", MyEvent());
}
请注意,如果要格式化为字符串,最好使用
fmt::format
而不是 fmt::format_to
。事实上,您可以将 StringFromArgs
替换为 fmt::format
。