Search code examples
c++this

Do all well-known C++ compilers implicitly pass "this" pointer as the 1st parameter to member functions?


I found a "trick" from here to cast any type into another type by using:

template<typename OUT, typename IN>
OUT ForceCast(IN in)
{
    union
    {
        IN  in;
        OUT out;
    }
    u = { in };

    return u.out;
};

Then I tried the code below:

class C1
{
    public:
    void Test(void)
    {
        std::cout<<"Hello!"<<std::endl;
    }
};

int main()
{
   C1 obj;
   void* function = ForceCast<void*>(&C1::Test);
   ((void(*)(void*))function)(&obj);
   
   return 0;
}

I compiled the code with MSVC without errors, and it ran as expected with an output of Hello!.

As I know, C++ compilers will implicitly pass this pointer into member functions. And the answer here in the last 2 paragraphs says all ABIs that the author heard of do pass the this pointer as the 1st parameter.

So I have 2 questions:

  1. Is it true that modern C++ compilers, including those for embedded systems (like ARMCC etc), pass the this pointer as the 1st parameter?

  2. I want to use this "trick" to build an easy-to-use and low overhead callback system without using the "heavy" std::function. If the 1st question is true, is there any other potential side effect with this "trick"? (Sample code: here)


Solution

  • Ignoring whether or not it's guaranteed for now (somebody else can certainly give you the details on what the standard say, and what other compilers do) - even practically, just on MSVC, you have one problem. The x64-windows calling convention does pass "this" as the first argument, however there is one major difference between a member-function and a free function:

    In case of a return-value, that cannot be returned in RAX, a pointer has to passed to the function. In the case of a free-function, MSVC mandates that this pointer is passed as the first register (RCX), and all other arguments get shifted by 1. For a member-function, this pointer has to be passed in the first register after the "this" argument (RDX), and all other arguments are shifted by 1. I know this because I recently implement the native x64-calling convention for my JIT compiler, and ran into the exact same problem - where I had to change up the order of parameters for return-handling, depending on whether it's a member or a free function.

    Pretty much means that, what you attempt cannot be generalized, even on just MSVC - it will never work to implement something like std::function, which has to deal with different types of return-values). That is not even talking about other issues with member-function pointers not actually being regular pointers at all - MSVC will for example make those pointers larger, when multiplex/virtual inheritance is in play, where more data is required to fixup "this".