Можно ли исправить указатели функции-члена iostream cout / cerr, выводимые как 1 или true? - PullRequest
3 голосов
/ 10 января 2020

Если вы выполните следующее:

#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 Значение арифметического c , перечисление, указатель или указатель на тип члена могут быть преобразованы в значение типа 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 или правда? Цель состоит в том, чтобы работать с любой свободной функцией или функцией класса-члена без необходимости вручную преобразовывать их в указатель (void *) перед отправкой их в std::cout или std::cerr.


Связанные вопросы:

  1. Печать указателя с помощью
  2. Указатель на функцию-член, всегда печатается как «1»

Обновление

Я пытался следовать Дэн М. tip ( generi c указатель на функцию-член в качестве параметра шаблона ):

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 Ответ

2 голосов
/ 10 января 2020

Ваша перегрузка работает только для указателей на функции, потому что аргумент является указателем на функцию.

Она не работает для указателей на функции-члены, потому что указатели на функции-члены не являются указателями на функции, что может показаться странным. Вы можете использовать аналогичную перегрузку для указателей на функции-члены:

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* не допускается также на всех системах. Это условно поддерживаемая функция. Вероятно, он будет работать на всех системах, которые используют динамическое связывание c, по крайней мере.

PPS Добавление перегрузок к стандартному классу, где все аргументы являются стандартными классами или фундаментальными типами, возможно, потенциально несовместимо с будущим стандартом языка.

PPPS Получение адреса main технически запрещено.

...