Make ostream operators opt in to reduce the risk of ODR violations

This commit is contained in:
Victor Zverovich 2022-02-04 15:06:45 -08:00
parent 8a21e328b8
commit f055ebbd25
5 changed files with 35 additions and 43 deletions

View File

@ -1016,7 +1016,11 @@ struct fallback_formatter {
// Specifies if T has an enabled fallback_formatter specialization.
template <typename T, typename Char>
using has_fallback_formatter =
#ifdef FMT_DEPRECATED_OSTREAM
std::is_constructible<fallback_formatter<T, Char>>;
#else
std::false_type;
#endif
struct view {};

View File

@ -105,6 +105,9 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
};
} // namespace detail
template <typename T, typename Char = char>
using ostream_formatter = detail::fallback_formatter<T, Char>;
FMT_MODULE_EXPORT
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,

View File

@ -53,6 +53,17 @@ std::ostream& operator<<(std::ostream& os, streamable_enum) {
enum unstreamable_enum {};
struct empty_test {};
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
namespace fmt {
template <> struct formatter<test_string> : ostream_formatter<test_string> {};
template <> struct formatter<date> : ostream_formatter<date> {};
template <>
struct formatter<streamable_enum> : ostream_formatter<streamable_enum> {};
template <> struct formatter<empty_test> : ostream_formatter<empty_test> {};
} // namespace fmt
TEST(ostream_test, enum) {
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
@ -86,9 +97,6 @@ TEST(ostream_test, format_specs) {
EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2));
}
struct empty_test {};
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
TEST(ostream_test, empty_custom_output) {
EXPECT_EQ("", fmt::format("{}", empty_test()));
}
@ -184,6 +192,9 @@ template <typename T> struct formatter<test_template<T>> : formatter<int> {
return formatter<int>::format(2, ctx);
}
};
template <>
struct formatter<fmt_test::abc> : ostream_formatter<fmt_test::abc> {};
} // namespace fmt
TEST(ostream_test, template) {
@ -214,41 +225,6 @@ TEST(ostream_test, disable_builtin_ostream_operators) {
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
}
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_string_like) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator fmt::detail::std_string_view<char>() const {
return {"foo", 3u};
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct streamable_and_convertible_to_bool {
operator bool() const { return true; }
};
@ -285,6 +261,10 @@ std::ostream& operator<<(std::ostream& os, copyfmt_test) {
return os << "foo";
}
namespace fmt {
template <> struct formatter<copyfmt_test> : ostream_formatter<copyfmt_test> {};
} // namespace fmt
TEST(ostream_test, copyfmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
}
@ -306,6 +286,10 @@ struct abstract {
}
};
namespace fmt {
template <> struct formatter<abstract> : ostream_formatter<abstract> {};
} // namespace fmt
void format_abstract_compiles(const abstract& a) {
fmt::format(FMT_COMPILE("{}"), a);
}

View File

@ -11,7 +11,6 @@
#include <climits>
#include <cstring>
#include "fmt/ostream.h"
#include "fmt/xchar.h"
#include "gtest-extra.h"
#include "util.h"
@ -533,10 +532,6 @@ TEST(printf_test, wide_string) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
}
TEST(printf_test, printf_custom) {
EXPECT_EQ("abc", test_sprintf("%s", test_string("abc")));
}
TEST(printf_test, vprintf) {
fmt::format_arg_store<fmt::printf_context, int> as{42};
fmt::basic_format_args<fmt::printf_context> args(as);

View File

@ -220,6 +220,12 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) {
return os << L"streamable_enum";
}
namespace fmt {
template <>
struct formatter<streamable_enum, wchar_t>
: ostream_formatter<streamable_enum, wchar_t> {};
} // namespace fmt
enum unstreamable_enum {};
TEST(xchar_test, enum) {