3

If you run the following:

#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

If you remove the std::cout.setf(std::ios::boolalpha) call, it just prints 1 instead of true.

If you look at the https://godbolt.org/z/6CFH3P assembly, you will notice that the C++ template resolution is choosing the boolean operator std::basic_ostream<char, std::char_traits<char> >::operator<<(bool).

After searching, I found a solution on the question 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.

This is the only conversion specified for function pointers.

However, it does not work for member class function pointers: 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

Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true? The goal would be to work with any free function or member class function without having to manually convert them to (void *) pointer before sending them to std::cout or std::cerr.


Related questions:

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

Update

I tried following 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;
}

But it throws out this warning: 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

How can I properly fix the warning warning: converting from ‘int (test_debugger::*)()’ to ‘void*’ [-Wpmf-conversions]?

Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
  • 2
    Note, that taking address of `main` is _UB_ in C++. – Dan M. Jan 10 '20 at 17:02
  • Your second code doesn't compile. – David G Jan 10 '20 at 17:02
  • 1
    @DanM. why is taking the address of `main` UB? – bolov Jan 10 '20 at 17:03
  • No, it is not possible. `std::ostream` has overloads for `operator <<` taking function pointer (making e.g. `std::cout << std::endl;` possible). You have to cast pointer to `void*` to get the address. And prefer C++ casting with `static_cast` rather than C-style casting. – Yksisarvinen Jan 10 '20 at 17:04
  • @user have you tried parametrizing ostream << operator like so https://stackoverflow.com/questions/9779105/generic-member-function-pointer-as-a-template-parameter ? – Dan M. Jan 10 '20 at 17:07
  • @bolov https://stackoverflow.com/questions/28567869/address-of-function-main-in-c-c – Dan M. Jan 10 '20 at 17:07
  • 1
    @DanM. It is ill-formed, not UB. The compiler is required to diagnose it. – Brian Bi Jan 10 '20 at 17:12
  • @DanM. thank you, didn't know that. However it looks like DanM is right, it is ill-formed rather than UB and gcc allows it via an extension. – bolov Jan 10 '20 at 17:44

1 Answers1

2

Your overload works only for function pointers because the argument is a function pointer.

It doesn't work for member function pointers because member function pointers are not function pointers, as confusing as that might be. You could use a similar overload for member function pointers:

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

However, member function pointers are not convertible to void* so you cannot print them using void*. You need to decide what you would like to print in their case. If your goal is to get just some output that might hopefully be related to what member function is being pointed at, then you could do something like:

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. Converting a function pointer to void* is not allowed on all systems either. It is a conditionally supported feature. It'll probably work on all systems that use dynamic linking at least.

P.P.S. Adding overloads to standard class where all arguments are standard classes or fundamental types is probably potentially incompatible with a future language standard.

P.P.P.S. Taking address of main is technically not allowed.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I got this error when trying to use your conversion: **`error: invalid cast from type 'void (test_debugger::*)()' to type 'unsigned char*'`** https://godbolt.org/z/HvkqVA – Evandro Coan Jan 16 '20 at 00:50
  • @user I fixed the example. – eerorika Jan 16 '20 at 03:30
  • For printing member class function pointer correctly, you also need consider whether your architecture big/little endian: [Why member function address are so far away from free functions?](https://stackoverflow.com/questions/59779964/why-member-function-address-are-so-far-away-from-free-functions) – Evandro Coan Jan 21 '20 at 20:14
  • @user Why do you need to consider that? You don't have any information about what those bytes represent, so knowing about endianness would not be of much use. – eerorika Jan 21 '20 at 20:16