From e6ef927e6be2e96ac32c0f477e2491a93af7a97a Mon Sep 17 00:00:00 2001 From: Mike Crowe Date: Tue, 9 Feb 2021 15:35:16 +0000 Subject: [PATCH] fmt::ptr: Support function pointers (#2131) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passing a function pointer to fmt::ptr results in: In file included from /home/mac/git/fmt/test/gmock/gmock.h:238, from /home/mac/git/fmt/test/format-test.cc:31: .../fmt/test/format-test.cc: In member function ‘virtual void FormatterTest_FormatPointer_Test::TestBody()’: .../fmt/test/format-test.cc:1486:56: error: no matching function for call to ‘ptr(void (&)(int, double, std::__cxx11::string))’ format("{}", fmt::ptr(function_pointer_test))); with GCC and Clang. Let's add an overload to support that usage. Unfortunately, MSVC would consider the overload to be ambiguous for unknown reasons: D:\a\fmt\fmt\test\format-test.cc(1485,1): error C2668: 'fmt::v7::ptr': ambiguous call to overloaded function [D:\a\fmt\build\test\format-test.vcxproj] D:\a\fmt\fmt\include\fmt/format.h(3742,60): message : could be 'const void *fmt::v7::ptr(T (__cdecl *)(int,double,std::string))' [D:\a\fmt\build\test\format-test.vcxproj] with [ T=void ] D:\a\fmt\fmt\include\fmt/format.h(3735,42): message : or 'const void *fmt::v7::ptr(T (__cdecl *))' [D:\a\fmt\build\test\format-test.vcxproj] with [ T=void (int,double,std::string) ] D:\a\fmt\fmt\test\format-test.cc(1486,1): message : while trying to match the argument list '(overloaded-function)' [D:\a\fmt\build\test\format-test.vcxproj] but luckily this means that the overload is unnecessary in that case anyway, so we can just make it conditional. --- doc/api.rst | 1 + include/fmt/format.h | 7 +++++++ test/format-test.cc | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index f89d13a806e6..6616818d6842 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -314,6 +314,7 @@ Utilities .. doxygenfunction:: fmt::ptr(const T *p) .. doxygenfunction:: fmt::ptr(const std::unique_ptr &p) .. doxygenfunction:: fmt::ptr(const std::shared_ptr &p) +.. doxygenfunction:: fmt::ptr(T (*fn)(Args...)) .. doxygenfunction:: fmt::to_string(const T &value) diff --git a/include/fmt/format.h b/include/fmt/format.h index 06c2f63f4fd7..de0a379ee0ad 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3739,6 +3739,13 @@ template inline const void* ptr(const std::unique_ptr& p) { template inline const void* ptr(const std::shared_ptr& p) { return p.get(); } +#if !FMT_MSC_VER +// MSVC lets function pointers decay to void pointers, so this +// overload is unnecessary. +template inline const void* ptr(T (*fn)(Args...)) { + return detail::bit_cast(fn); +} +#endif class bytes { private: diff --git a/test/format-test.cc b/test/format-test.cc index 37c22e1afc84..684dd1b8c0e1 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1467,6 +1467,9 @@ TEST(FormatterTest, FormatUCharString) { EXPECT_EQ("test", format("{0:s}", ptr)); } +void function_pointer_test(int, double, std::string) { +} + TEST(FormatterTest, FormatPointer) { check_unknown_types(reinterpret_cast(0x1234), "p", "pointer"); EXPECT_EQ("0x0", format("{0}", static_cast(nullptr))); @@ -1479,6 +1482,8 @@ TEST(FormatterTest, FormatPointer) { EXPECT_EQ(format("{}", fmt::ptr(up.get())), format("{}", fmt::ptr(up))); std::shared_ptr sp(new int(1)); EXPECT_EQ(format("{}", fmt::ptr(sp.get())), format("{}", fmt::ptr(sp))); + EXPECT_EQ(format("{}", fmt::detail::bit_cast(&function_pointer_test)), + format("{}", fmt::ptr(function_pointer_test))); EXPECT_EQ("0x0", format("{}", nullptr)); }