Можно ли исправить указатели функций-членов iostream cout/cerr, которые печатаются как 1 или 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)
.
После поиска я нашел решение по вопросу Как распечатать указатели функций с помощью cout?
Стандарт C++ определяет:
4.12 Булевы преобразования
1 Значение r арифметики, перечисления, указателя или указателя на тип элемента может быть преобразовано в значение типа bool.
Это единственное преобразование, указанное для указателей функций.
Однако это не работает для указателей функций класса-члена: 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? Целью было бы работать с любой бесплатной функцией или функцией класса-члена без необходимости вручную преобразовывать их в(void *)
указатель перед отправкой в std::cout
или std::cerr
.
Связанные вопросы:
Обновить
Я попробовал следовать Dan M.. ( указатель на общую функцию-член в качестве параметра шаблона):
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]
?
1 ответ
Ваша перегрузка работает только для указателей на функции, потому что аргумент является указателем на функцию.
Это не работает для указателей на функции-члены, потому что указатели на функции-члены не являются указателями на функции, как бы это ни сбивало с толку. Вы можете использовать аналогичную перегрузку для указателей на функции-члены:
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];
PS Преобразование указателя функции в void*
также разрешено не во всех системах. Это условно поддерживаемая функция. Вероятно, он будет работать во всех системах, которые хотя бы используют динамическое связывание.
PPS Добавление перегрузок в стандартный класс, где все аргументы являются стандартными классами или фундаментальными типами, вероятно, потенциально несовместимо с будущим языковым стандартом.
PPPS Принятие адреса main
технически не допускается.