Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added formattable concept #3974

Merged
merged 1 commit into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/fmt/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ import std;
# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
#endif

// Detect C++20 concepts
#ifdef FMT_USE_CONCEPTS
// Use the provided definition.
#elif defined(__cpp_concepts)
# define FMT_USE_CONCEPTS 1
#else
# define FMT_USE_CONCEPTS 0
#endif

// Check if exceptions are disabled.
#ifdef FMT_EXCEPTIONS
// Use the provided definition.
Expand Down Expand Up @@ -2007,6 +2016,11 @@ using is_formattable = bool_constant<!std::is_base_of<
detail::unformattable, decltype(detail::arg_mapper<buffered_context<Char>>()
.map(std::declval<T&>()))>::value>;

#if FMT_USE_CONCEPTS
template <typename T, typename Char = char>
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
vitaut marked this conversation as resolved.
Show resolved Hide resolved
#endif

/**
\rst
Constructs an object that stores references to arguments and can be implicitly
Expand Down
70 changes: 70 additions & 0 deletions test/base-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,64 @@ TEST(core_test, is_formattable) {
static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, "");
}

#if FMT_USE_CONCEPTS
TEST(core_test, formattable) {
static_assert(fmt::formattable<char>);
static_assert(fmt::formattable<char&>);
static_assert(fmt::formattable<char&&>);
static_assert(fmt::formattable<const char>);
static_assert(fmt::formattable<const char&>);
static_assert(fmt::formattable<const char&&>);

static_assert(fmt::formattable<fmt::basic_string_view<char>>);
static_assert(fmt::formattable<fmt::basic_string_view<char>&>);
static_assert(fmt::formattable<fmt::basic_string_view<char>&&>);
static_assert(fmt::formattable<const fmt::basic_string_view<char>>);
static_assert(fmt::formattable<const fmt::basic_string_view<char>&>);
static_assert(fmt::formattable<const fmt::basic_string_view<char>&&>);

static_assert(!fmt::formattable<wchar_t>);
# ifdef __cpp_char8_t
static_assert(!fmt::formattable<char8_t>);
# endif
static_assert(!fmt::formattable<char16_t>);
static_assert(!fmt::formattable<char32_t>);
static_assert(!fmt::formattable<signed char*>);
static_assert(!fmt::formattable<unsigned char*>);
static_assert(!fmt::formattable<const signed char*>);
static_assert(!fmt::formattable<const unsigned char*>);
static_assert(!fmt::formattable<const wchar_t*>);
static_assert(!fmt::formattable<const wchar_t[3]>);
static_assert(!fmt::formattable<fmt::basic_string_view<wchar_t>>);
static_assert(fmt::formattable<enabled_formatter>);
static_assert(!fmt::formattable<enabled_ptr_formatter*>);
static_assert(!fmt::formattable<disabled_formatter>);
static_assert(!fmt::formattable<disabled_formatter_convertible>);

static_assert(fmt::formattable<const_formattable&>);
static_assert(fmt::formattable<const const_formattable&>);

static_assert(fmt::formattable<nonconst_formattable&>);
# if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
static_assert(!fmt::formattable<const nonconst_formattable&>);
# endif

static_assert(!fmt::formattable<convertible_to_pointer>);
const auto f = convertible_to_pointer_formattable();
auto str = std::string();
fmt::format_to(std::back_inserter(str), "{}", f);
EXPECT_EQ(str, "test");

static_assert(!fmt::formattable<void (*)()>);

struct s;
static_assert(!fmt::formattable<int(s::*)>);
static_assert(!fmt::formattable<int (s::*)()>);
static_assert(!fmt::formattable<unformattable_scoped_enum>);
static_assert(!fmt::formattable<unformattable_scoped_enum>);
}
#endif

TEST(core_test, format_to) {
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{}", 42);
Expand Down Expand Up @@ -757,6 +815,9 @@ struct implicitly_convertible_to_string_view {
TEST(core_test, no_implicit_conversion_to_string_view) {
EXPECT_FALSE(
fmt::is_formattable<implicitly_convertible_to_string_view>::value);
#if FMT_USE_CONCEPTS
static_assert(!fmt::formattable<implicitly_convertible_to_string_view>);
#endif
}

#ifdef FMT_USE_STRING_VIEW
Expand All @@ -767,6 +828,9 @@ struct implicitly_convertible_to_std_string_view {
TEST(core_test, no_implicit_conversion_to_std_string_view) {
EXPECT_FALSE(
fmt::is_formattable<implicitly_convertible_to_std_string_view>::value);
# if FMT_USE_CONCEPTS
static_assert(!fmt::formattable<implicitly_convertible_to_std_string_view>);
# endif
}
#endif

Expand All @@ -781,6 +845,9 @@ TEST(core_test, format_explicitly_convertible_to_string_view) {
// default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
# if FMT_USE_CONCEPTS
static_assert(!fmt::formattable<explicitly_convertible_to_string_view>);
# endif
}

# ifdef FMT_USE_STRING_VIEW
Expand All @@ -794,6 +861,9 @@ TEST(core_test, format_explicitly_convertible_to_std_string_view) {
static_assert(
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
"");
# if FMT_USE_CONCEPTS
static_assert(!fmt::formattable<explicitly_convertible_to_std_string_view>);
# endif
}
# endif
#endif
Expand Down
Loading