是否可以修复打印为 1 或 true 的 iostream cout/cerr 成员函数指针?

Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true?

如果您运行以下情况:

#include <iostream>

int main()
{
    std::cout.setf(std::ios::boolalpha);
    std::cout << &main << "\n";
    std::cout << (void*)&main << "\n";  // The workaround
    return 0;
}

// prints something like
//   true
//   0x55deee04189a

如果删除 std::cout.setf(std::ios::boolalpha) 调用,它只会打印 1 而不是 true

如果您查看 https://godbolt.org/z/6CFH3P 程序集,您会注意到 C++ 模板解析正在选择布尔运算符 std::basic_ostream<char, std::char_traits<char> >::operator<<(bool)

经过搜索,我找到了问题的解决方案How to print function pointers with cout?

The C++ Standard specifies:

4.12 Boolean conversions

1 An rvalue of arithmetic, enumeration, pointer, or pointer to member type can be converted to an rvalue of type bool.

这是为函数指针指定的唯一转换。

但是,它不适用于成员class函数指针:https://godbolt.org/z/zBN5Va

#include<iostream>

template<class Ret, class... Args>
std::ostream& operator <<(std::ostream& os, Ret(*p)(Args...) ) {
    return os << "funptr " << (void*)p;
}

struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}

int main() {
    std::cout << "0. " << &test_debugger::var << std::endl;
    std::cout << "1. " << fun_void_void << std::endl;
    std::cout << "2. " << fun_void_double << std::endl;
    std::cout << "3. " << fun_double_double << std::endl;
}

// Prints:
//    0. 1
//    1. funptr 0x100401080
//    2. funptr 0x100401087
//    3. funptr 0x100401093

是否可以修复 iostream cout/cerr 成员函数指针打印为 1 或 true 的问题?目标是使用任何自由函数或成员 class 函数,而无需在将它们发送到 std::coutstd::cerr.[=32 之前手动将它们转换为 (void *) 指针=]


相关问题:

  1. Printing a pointer with <iostream>
  2. Pointer to member function, always prints as "1"

更新

我尝试关注 Dan M. tip (generic member function pointer as a template parameter):

template <typename T, typename R, typename ...Args>
std::ostream& operator <<(std::ostream& os, R (T::*p)(Args...) ) {
    return os << "funptr " << (void*)p;
}

但它抛出了这个警告:https://godbolt.org/z/yj52hM

$ g++ -o main.exe --std=c++11 test_debugger.cpp && ./main.exe
test_debugger.cpp: In instantiation of ‘std::ostream& operator<<(std::ostream&, R (T::*)(Args ...)) [with T = test_debugger; R = int; Args = {}; std::ostream = std::basic_ostream<char>]’:
test_debugger.cpp:19:42:   required from here
test_debugger.cpp:10:31: warning: converting from ‘int (test_debugger::*)()’ to ‘void*’ [-Wpmf-conversions]
     return os << "funptr " << (void*)p;
                               ^~~~~~~~
0. funptr 0x100401860
1. funptr 0x100401080
2. funptr 0x100401087
3. funptr 0x100401093

如何正确修复警告 warning: converting from ‘int (test_debugger::*)()’ to ‘void*’ [-Wpmf-conversions]

您的重载仅适用于函数指针,因为参数是函数指针。

它不适用于成员函数指针,因为成员函数指针不是函数指针,尽管这可能会造成混淆。您可以对成员函数指针使用类似的重载:

template<class C, class Ret, class... Args>
std::ostream& operator <<(std::ostream& os, Ret (C::*p)(Args...)) {
    return os << "memfunptr " << "something...";
}

但是,成员函数指针不能转换为 void*,因此您不能使用 void* 打印它们。您需要决定要在他们的案例中打印什么。如果您的目标只是获得一些可能与所指向的成员函数相关的输出,那么您可以执行以下操作:

unsigned char* internal_representation = reinterpret_cast<unsigned char*>(&p);
for(std::size_t i = 0; i < sizeof p; i++)
    os << std::hex << (int)internal_representation[i];

P.S。在所有系统上也不允许将函数指针转换为 void*。这是一个有条件支持的功能。它可能适用于至少使用动态链接的所有系统。

P.P.S。向标准 class 添加重载,其中所有参数都是标准 classes 或基本类型可能与未来的语言标准不兼容。

P.P.P.S。技术上不允许使用 main 地址。