I have a simple class that has a tostring() method:
class MyClass {
public:
std::string tostring() const;
static iterator begin();
static iterator end();
};
Although I'm using the fmt library now, this code is ported from code that did not, so many of the legacy classes implement a tostring() method, and I have a template that will generate a fmt::formatter for any class that has that method. It's been working fine.
This particular class, however, also has begin/end functions. They're static (this class is similar to an enumeration and you can iterate through all possible values), though, and shouldn't have anything to do with formatting.
Everything was okay until I needed to include fmt/ranges.h for some different code. The problem is that there is a range formatter that sees the begin/end functions and wants to format the class as a range. Now, if I try to format the class, I get an ambiguous instantiation of the formatter (one for the template I want to use and one for the range formatter).
Is there a way to get the range formatter to ignore this class?
A complete example is:
#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;
}
If I have a full specialization of fmt::formatter for MyClass, then there is no ambiguity, but if I use a partial specialization as I do in the example, then uncommenting the "#include <fmt/ranges.h>" will cause an ambiguous template instantiation.