有没有办法为用户定义的 class 抑制 fmt 范围格式化程序?
Is there a way to suppress the fmt range formatter for a user defined class?
我有一个简单的 class,它有一个 tostring() 方法:
class MyClass {
public:
std::string tostring() const;
static iterator begin();
static iterator end();
};
虽然我现在使用的是 fmt 库,但这段代码是从没有使用的代码移植过来的,所以很多遗留 classes 都实现了 tostring() 方法,我有一个模板可以为任何具有该方法的 class 生成一个 fmt::formatter。一直很好用。
然而,这个特定的 class 也具有 begin/end 功能。它们是静态的(这 class 类似于枚举,您可以遍历所有可能的值),但与格式无关。
一切正常,直到我需要为一些不同的代码包含 fmt/ranges.h。问题是有一个范围格式化程序看到 begin/end 函数并希望将 class 格式化为一个范围。现在,如果我尝试格式化 class,我会得到格式化程序的模棱两可的实例化(一个用于我要使用的模板,一个用于范围格式化程序)。
有没有办法让范围格式化程序忽略这个 class?
一个完整的例子是:
#include <type_traits>
#include <utility>
#include <string>
#include <vector>
#include <fmt/format.h>
// #include <fmt/ranges.h>
// Create formatter for any class that has a tostring() method
template <typename T>
struct has_tostring_member {
private:
template <typename U>
static std::true_type test( decltype(&U::tostring) );
template <typename U>
static std::false_type test(...);
public:
using result = decltype(test<T>(0) );
static constexpr bool value = result::value;
};
template <typename T, typename Char>
struct fmt::formatter<T, Char,
std::enable_if_t<has_tostring_member<T>::value > >
: formatter<basic_string_view<Char>, Char> {
template <typename FormatContext>
auto
format( const T& e, FormatContext& ctx )
{
return formatter<string_view>::format( e.tostring(), ctx );
}
};
class MyClass
{
public:
explicit MyClass(int i) : value(i) {}
std::string tostring() const { return std::to_string(value); }
static auto begin() { return std::begin(static_data); }
static auto end() { return std::end(static_data); }
private:
int value;
static const std::vector<std::string> static_data;
};
const std::vector<std::string> MyClass::static_data{ "a", "b", "c" };
int main(void) {
MyClass c{10};
fmt::print("c is {}\n", c);
return 0;
}
如果我对 MyClass 进行了 fmt::formatter 的完全特化,那么就没有歧义,但是如果我像示例中那样使用部分特化,则取消注释“#include " 将导致模板实例化不明确。
以下是选项(您已经发现):
- 不包括
fmt/ranges.h
。
- 提供
formatter
的完整专业化。
请注意,如果您已经为具有 tostring
的所有类型实现了通用格式化程序,您可以在一行中执行 (2) (https://godbolt.org/z/cW3WzaP6f):
template <> struct fmt::formatter<MyClass> : tostring_formatter<MyClass> {};
我有一个简单的 class,它有一个 tostring() 方法:
class MyClass {
public:
std::string tostring() const;
static iterator begin();
static iterator end();
};
虽然我现在使用的是 fmt 库,但这段代码是从没有使用的代码移植过来的,所以很多遗留 classes 都实现了 tostring() 方法,我有一个模板可以为任何具有该方法的 class 生成一个 fmt::formatter。一直很好用。
然而,这个特定的 class 也具有 begin/end 功能。它们是静态的(这 class 类似于枚举,您可以遍历所有可能的值),但与格式无关。
一切正常,直到我需要为一些不同的代码包含 fmt/ranges.h。问题是有一个范围格式化程序看到 begin/end 函数并希望将 class 格式化为一个范围。现在,如果我尝试格式化 class,我会得到格式化程序的模棱两可的实例化(一个用于我要使用的模板,一个用于范围格式化程序)。
有没有办法让范围格式化程序忽略这个 class?
一个完整的例子是:
#include <type_traits>
#include <utility>
#include <string>
#include <vector>
#include <fmt/format.h>
// #include <fmt/ranges.h>
// Create formatter for any class that has a tostring() method
template <typename T>
struct has_tostring_member {
private:
template <typename U>
static std::true_type test( decltype(&U::tostring) );
template <typename U>
static std::false_type test(...);
public:
using result = decltype(test<T>(0) );
static constexpr bool value = result::value;
};
template <typename T, typename Char>
struct fmt::formatter<T, Char,
std::enable_if_t<has_tostring_member<T>::value > >
: formatter<basic_string_view<Char>, Char> {
template <typename FormatContext>
auto
format( const T& e, FormatContext& ctx )
{
return formatter<string_view>::format( e.tostring(), ctx );
}
};
class MyClass
{
public:
explicit MyClass(int i) : value(i) {}
std::string tostring() const { return std::to_string(value); }
static auto begin() { return std::begin(static_data); }
static auto end() { return std::end(static_data); }
private:
int value;
static const std::vector<std::string> static_data;
};
const std::vector<std::string> MyClass::static_data{ "a", "b", "c" };
int main(void) {
MyClass c{10};
fmt::print("c is {}\n", c);
return 0;
}
如果我对 MyClass 进行了 fmt::formatter 的完全特化,那么就没有歧义,但是如果我像示例中那样使用部分特化,则取消注释“#include
以下是选项(您已经发现):
- 不包括
fmt/ranges.h
。 - 提供
formatter
的完整专业化。
请注意,如果您已经为具有 tostring
的所有类型实现了通用格式化程序,您可以在一行中执行 (2) (https://godbolt.org/z/cW3WzaP6f):
template <> struct fmt::formatter<MyClass> : tostring_formatter<MyClass> {};