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)); }