Why member function address are so far away from free functions?
Asked Answered
V

2

1

Taking this example: https://godbolt.org/z/gHqCSA

#include<iostream>

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

template <typename ClassType, typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return (ClassType::*p)(Args...) )
{
    unsigned char* internal_representation = reinterpret_cast<unsigned char*>(&p);
    os << "0x" << std::hex;

    for(int i = 0; i < sizeof p; i++) {
        os << (int)internal_representation[i];
    }

    return os;
}
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. 0x7018400100000000000
//    1. 0x100401080
//    2. 0x100401087
//    3. 0x100401093

I see the address of the member function is 0x7018400100000000000, which is understandable because member functions pointers have 16 bytes while free function as 0x100401080 have only 8 bytes.

However, why the member function address 0x7018400100000000000 is so far away from free function address 0x100401080? i.e., |0x7018400100000000000 - 0x100401080| = 0x70184000FFEFFBFEF80?

Why it is not closer i.e., something like 0x100401... instead of 0x701840...? Or I am printing the member function address wrong?

Valenta answered 17/1, 2020 at 1:13 Comment(3)
your mistake is thinking that member function addresses have anything to do with memory addressesVerein
printing a btye (4) as an int will be "4" instead of "04" needed to make sense of memory as hex, use setw and setfill as wellSandie
A pointer-to-member-function is not a pointer. When you cast it to an int you get whatever is there, and having told the compiler to pretend that what's there is an int, you get what you asked for: a meaningless value.Estonian
C
3

Your architecture is little-endian. The low byte of the address is in the first byte of p, so your address is being printed out backwards.

Celebrity answered 17/1, 2020 at 1:42 Comment(2)
And as @Sandie pointed out in a comment on the question, without leading zeroes on any byte in the range 0x00 to 0x0f. Reverse the output, and add a few zeroes in the obvious places, and the address of the member function is right next door to the others.Korrie
Maybe. But a pointer-to-member-function is not a pointer, so there's no sense in pretending that it is.Estonian
V
0

Fixed code which automatically detects little/big endian: https://godbolt.org/z/XSvT5R

#include <iostream>
#include <iomanip>
#include <sstream>

inline bool is_big_endian() {
    long int longvalue = 1;

    // https://mcmap.net/q/20654/-detecting-endianness
    unsigned char* representation = reinterpret_cast<unsigned char*>(&longvalue);
    return ( (unsigned) representation[sizeof(long int) - 1] ) == 1;
}

template<typename Pointer>
std::ostream& print_pointer(std::ostream& os, const Pointer& pointer) {
    const unsigned char* representation = (unsigned char*) &pointer;

    int precision = 0;
    bool haszeros = false;

    unsigned firsthexdigit;
    unsigned secondhexdigit;

    std::ostringstream stream;
    stream.flags( os.flags() );
    stream << std::hex;
    #define print_pointer_HEX_DIGIT \
        firsthexdigit = (unsigned) representation[index] >> 4 & 0xf; \
        secondhexdigit = (unsigned) representation[index] & 0xf; \
        if( haszeros || firsthexdigit ) { \
            precision++; \
            haszeros = true ; \
            stream << firsthexdigit; \
        } \
        if( haszeros || secondhexdigit ) { \
            precision++; \
            haszeros = true ; \
            stream << secondhexdigit; \
        }

    if( is_big_endian() ) {
        for(int index = 0; index < static_cast<int>(sizeof pointer); index++) {
            print_pointer_HEX_DIGIT
        }
    }
    else {
        for(int index = static_cast<int>(sizeof pointer - 1); index >= 0 ; index--) {
            print_pointer_HEX_DIGIT
        }
    }

    if( os.precision() - ++precision > 0 ) {
        return os << "0x" + std::string( os.precision() - ++precision, '0' ) + stream.str();
    }
    return os << "0x" + stream.str();
}

template<typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return(*pointer)(Args...) ) {
    return print_pointer(os , pointer);
}

template <typename ClassType, typename Return, typename... Args>
std::ostream& operator <<(std::ostream& os, Return (ClassType::*pointer)(Args...) ) {
    return print_pointer(os , pointer);
}

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;
    std::cout << "4. " << std::setfill('0') << std::setw(16) << fun_void_void << std::endl;
    std::cout << "5. " << std::setprecision(16) << fun_void_double << std::endl;
}
// Prints:
//    0. 0x100402e80
//    1. 0x100401118
//    2. 0x10040111f
//    3. 0x10040112b
//    4. 000000x100401118
//    5. 0x0000010040111f
Valenta answered 21/1, 2020 at 1:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.