37
votes

Suppose I have something along the lines of

struct Foo {
    void goo() {printf("Test");}
}

external void _ZN3Foo3gooEv(Foo *f);

int main() {
        Foo f;
        _ZN3Foo3gooEv(&f);
}

Is it possible to call Foo::goo() through the name mangled version of the function here?

Edit:

As a clarification, this is just an experiment to see if it's possible to explicitly call a name mangled function. There is no further goal here.

I was thought that all member functions basically take the this pointer as their first argument.

I get that this won't link, but I don't get why. I thought that name mangling happens at compile time, and when the linker runs it resolves the calls to the name mangled function. (That's why I figured if we leave _ZN3Foo3gooEv as extern, it would go to the symbol table to look it up).

Am I misunderstanding something here?

1
This sounds like it may be an instance of the XY problem. Would you be able to share more details about your end goal in doing this? - nanofarad
I suspect this isn't possible, because the C++ compiler will mangle _ZN3Foo3gooEv into something else, and then it won't match anything. - Mooing Duck
You can't call it correctly without an instance anyway, so even if it linked, which it won't, it would never work. Certainly an XY problem here. - user207421
I don't have an end goal in doing this. This is a demo for knowledge's sake. Can you elaborate on what you mean by not being able to call goo without an instance? Isn't goo internally converted into some function that basically takes a pointer to a Foo object? (the this pointer) I assumed that f would serve as our instance here. - Henry
@MarquisofLorne There's also std::invoke, which lets you pass the instance as the first parameter, specifically for working with member function pointers like this, and there's also the bizzare member function pointer syntax (object).*(ptrToMember). - Mooing Duck

1 Answers

37
votes

You can, with some caveats.

You either have to use the member function in a way that code will be generated or have it be not inline, and your mangled definition should be extern "C" to prevent "double mangling". E.g.:

#include <cstdio>

struct Foo {
    const char* message;
    void goo();
};

void Foo::goo() {
    std::printf("%s", this->message);
}

extern "C" void _ZN3Foo3gooEv(Foo *f);

int main() {
        Foo f{ "Test" };
        _ZN3Foo3gooEv(&f);
}

will work fine and be stable specifically in gcc.

This works because the calling convention for member functions is equivalent to the default calling convention for free functions on most systems. this is passed to member functions as if it was the first argument, with explicit arguments taking the later arg-passing slots. (Registers and/or stack). I believe that this is true for x86-64, ARM 32-bit and 64-bit at least, and 32-bit x86 other than Windows.

clang seems to specifically support this use case: It inlines Foo::goo into main when gcc pretends that _ZN3Foo3gooEv and Foo::goo after mangling are two separate entities (and thus can't be substituted and inlined).

With MSVC, you can do something similar. However, in x86-32 code on windows, the calling convention __thiscall is used where instead of passing the this pointer as the first argument, it is passed in the ECX register with other args on the stack. If cross compiling for x86-32 with clang or gcc, you can use [[gnu::thiscall]] (__attribute__((thiscall))). (fastcall is similar if there's only one arg, but with 2 args would pass the first 2 in registers, not just the first 1).


But there really should be no reason to do this. It can only be viewed as a compiler extension (Since it uses _Capital symbols), and if you need a way to call these functions from C, use a helper void Foo_goo(struct Foo*) that you define in a C++ translation unit. It can also call private member functions, but you can already do this in a standards-compliant way with template specialisations.