如何解决printf 函数和 fmt 函数之间的重载
我有一个类在其构造函数中采用 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);
但是这个类被用在一百多个地方,“改变世界”是不可行的。所以我想保留两个构造函数,但显然现在我需要一种方法来在它们之间进行选择。我对必须添加新的虚拟参数或其他东西并不感到兴奋。
然后我想,好吧,我也很想强制使用 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。
解决方法
您可以按以下方式操作 (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();
}
};
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。