【问题标题】:Overloading between printf functions and fmt functionsprintf 函数和 fmt 函数之间的重载
【发布时间】:2021-09-07 12:02:11
【问题描述】:

我有一个类,它在其构造函数中采用一组 C printf 可变参数,如下所示:

class Foo {
public:
    Foo(const char* str, ...) __attribute__((format(printf, 2, 3)));

现在我希望能够在此类中使用fmt library。如果我愿意改变所有来电者,我可以像这样切换它:

class Foo {
public:
    template<typename Str, typename... Args>
    Foo(const Str& str, const Args&... args)
        : Foo(str, fmt::make_args_checked<Args...>(str, args...))
    {}

private:
    Foo(fmt::string_view fmt, fmt::format_args args);

但是这个类在 100 多个地方使用,“改变世界”是不可行的。所以我想保留这两个构造函数,但显然现在我需要一种方法来在它们之间进行选择。我对必须添加新的虚拟参数或其他东西并不感到兴奋。

然后我想,好吧,我也很想强制使用FMT_STRING() 宏,因为我的 printf 样式代码利用了 GCC 和 clang 中的 printf 格式检查。所以也许我可以用它做点什么:我可以创建自己的宏,比如MYFMT(),它会以某种方式调用 FMT_STRING(),或者至少做同样的检查,然后解析为我自己的类型,可以用来选择不同的构造函数;类似:

#define MYFMT(_f) ...
class Foo {
public:
    Foo(const char* str, ...);
    Foo(const MyType& str, ...) ...

所以,用法是这样的:

    auto x = Foo("this is a %s string", "printf");
    auto y = Foo(MYFMT("this is a {} string"), "fmt");

但我已经玩了几个小时,并试图围绕 FMT_STRING 宏的工作原理以及我需要做什么,但我什么也想不出来。也许由于某种原因这是不可能的,但如果有人有任何提示,那就太好了。

FWIW 我的基础编译器是 GCC 10、clang 9 和 MSVC 2019,所以我至少可以依赖 C++17。

【问题讨论】:

    标签: c++ fmt


    【解决方案1】:

    你可以这样做(godbolt):

    #include <fmt/core.h>
    
    struct format_string {
      fmt::string_view str;
    
      constexpr operator fmt::string_view() const { return str; }
    };
    
    #define MYFMT(s) format_string{s}
    
    class Foo {
     public:
      template <typename... T>
      Foo(const char* str, T&&... args) {
        fmt::print("printf\n");
      }
    
      template <typename... T>
      Foo(fmt::format_string<T...> str, T&&... args) {
        fmt::print("fmt\n");
      }
    };
    
    int main() {
      Foo("%d\n", 42);        // calls the printf overload
      Foo(MYFMT("{}\n"), 42); // calls the fmt overload
    }
    

    使用 C++20,这将在 {fmt} 中为您提供编译时检查。请注意,可变参数在 printf 重载中被可变参数模板替换以避免歧义,因此您将无法应用格式属性。稍微调整一下这个解决方案可能会保留可变参数。

    更好的选择是完全避免重载和宏,而是使用不同的函数:

    class Foo {
     private:
      Foo() {}
      
     public:
      Foo(const char* str, ...) {
        fmt::print("printf\n");
      }
    
      template <typename... T>
      static Foo format(fmt::format_string<T...> str, T&&... args) {
        fmt::print("fmt\n");
        return Foo();
      }
    };
    

    【讨论】:

    • 感谢您的信息;我会看看,看看我能不能让它工作。不幸的是,我无法在任何地方访问 C++20。此外,摆脱可变参数方法并不容易:它们不只是打印出一些东西:它们将文本生成到字符串缓冲区中,并且 va_list 被传递等等。我认为它不会在我的环境中工作让人们调用静态方法而不是构造函数,这就是为什么我希望避免使用不同类型的歧义。也许那行不通:我真的不明白使用什么魔法格式来实现编译时格式检查。
    • 您仍然可以在内部使用可变参数,但不能使用格式属性。
    猜你喜欢
    • 1970-01-01
    • 2013-02-14
    • 1970-01-01
    • 1970-01-01
    • 2016-03-02
    • 2016-04-10
    • 1970-01-01
    • 2015-08-01
    • 2019-01-27
    相关资源
    最近更新 更多