mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-25 19:50:05 +00:00
Make ostream operators opt in to reduce the risk of ODR violations
This commit is contained in:
parent
8a21e328b8
commit
f055ebbd25
@ -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 {};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user