diff --git a/include/fmt/core.h b/include/fmt/core.h index bb56da7f..de6164fe 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1146,6 +1146,9 @@ constexpr bool is_arithmetic_type(type t) { struct unformattable {}; struct unformattable_char : unformattable {}; struct unformattable_const : unformattable {}; +struct unformattable_function_pointer : unformattable {}; +struct unformattable_member_function_pointer : unformattable {}; +struct unformattable_member_object_pointer : unformattable {}; struct unformattable_pointer : unformattable {}; template struct string_value { @@ -1228,6 +1231,9 @@ template class value { value(unformattable_char); value(unformattable_const); value(unformattable_pointer); + value(unformattable_function_pointer); + value(unformattable_member_function_pointer); + value(unformattable_member_object_pointer); private: // Formats an argument of a custom type, such as a user-defined class. @@ -1368,11 +1374,32 @@ template struct arg_mapper { return val; } + template >::type>::value)> + FMT_CONSTEXPR auto map(const T&) -> unformattable_function_pointer { + return {}; + } + + template ::value)> + FMT_CONSTEXPR auto map(const T&) -> unformattable_member_object_pointer { + return {}; + } + + template ::value)> + FMT_CONSTEXPR auto map(const T&) -> unformattable_member_function_pointer { + return {}; + } + // We use SFINAE instead of a const T* parameter to avoid conflicting with // the C array overload. + template < typename T, - FMT_ENABLE_IF(std::is_convertible::value && + FMT_ENABLE_IF(!std::is_function>::type>::value && + std::is_convertible::value && !std::is_convertible::value)> FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { return {}; @@ -1677,6 +1704,27 @@ FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { static_assert(formattable_pointer, "Formatting of non-void pointers is disallowed."); + // Formatting of function pointers is disallowed. + constexpr bool formattable_function_pointer = + !std::is_same::value; + static_assert(formattable_function_pointer, + "Formatting of function pointers is disallowed."); + + // Formatting of member function pointers is disallowed. + constexpr bool formattable_member_function_pointer = + !std::is_same::value; + static_assert(formattable_member_function_pointer, + "Formatting of member function pointers is disallowed."); + + // Formatting of member object pointers is disallowed. + constexpr bool formattable_member_object_pointer = + !std::is_same::value; + static_assert(formattable_member_object_pointer, + "Formatting of member object pointers is disallowed."); + constexpr bool formattable = !std::is_same::value; static_assert( diff --git a/test/compile-error-test/CMakeLists.txt b/test/compile-error-test/CMakeLists.txt index 8202f279..44bbb1ab 100644 --- a/test/compile-error-test/CMakeLists.txt +++ b/test/compile-error-test/CMakeLists.txt @@ -67,6 +67,12 @@ expect_compile_error(" fmt::format(\"{}\", S()); ") +# Formatting a function +expect_compile_error(" + void (*f)(); + fmt::format(\"{}\", f); +") + # Make sure that compiler features detected in the header # match the features detected in CMake. if (SUPPORTS_USER_DEFINED_LITERALS) diff --git a/test/core-test.cc b/test/core-test.cc index da3bc0f9..e1383e84 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -770,6 +770,13 @@ TEST(core_test, is_formattable) { static_assert(!fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); static_assert(!fmt::is_formattable::value, ""); + + static_assert(!fmt::is_formattable::value, ""); + + struct S; + + static_assert(!fmt::is_formattable::value, ""); + static_assert(!fmt::is_formattable::value, ""); } TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }