C++ FMT 问题格式化自定义摘要 class

C++ FMT issue formatting a custom abstract class

我正在为一个个人项目开发一个事件系统,我试图让事件记录到控制台就像 LOG(event) 一样简单。

在这种情况下,事件由一个 Event class 定义,它有一些方法和一个 virtual ToString() 函数,returns 一个带有事件信息的字符串等等我喜欢在每个事件上输出。 class 通过定义继承自 Event class 的特定事件 class 进一步扩展,并根据每个事件的作用和内容定义 ToString() 函数它有变量。

所以我的 LOG 宏调用静态 Log class' 函数通过将参数转换为字符串在控制台中显示消息,如下所示:

#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 指南制作我想要的事件日志会很容易,我的想法是为事件设置格式化程序 class:

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);
    }

};

但是,当我尝试对特定事件 class 执行 LOG(event) 时,这不起作用(假设“事件”是 WindowResizedEvent 继承自 Event class) 我一直有来自 fmt 的相同错误,因为它无法格式化事件(我还尝试在 Event class 中添加一个 const char*() 运算符,但仍然相同):

“论点 2”是“事件”论点(正如我所说,中间还有一些其他论点在这个问题上并不重要)。

有谁知道如何在不为每种类型的事件指定格式化程序的情况下对其进行格式化 class?因为对于每种事件类型,它始终是相同的代码。

格式字符串应作为 fmt::format_string 而不是模板参数传递。这是一个工作示例 (https://godbolt.org/z/xYehaMWsG):

#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) {
    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