C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Return Value Optimisation whenever you need it (guaranteed elision)

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Sun, 16 Jul 2023 21:22:29 +0100
On Sun, Jul 16, 2023 at 5:09 PM Arthur O'Dwyer wrote:
>
> The problem with this — and with the inline-assembly stuff — is that it's not portable C++


I've had an epiphany.

I was about to start writing assembler for Microsoft x64, cdecl,
stdcall, aarch64, arm32 . . . and I had started by reading through
Microsoft's description of its 64-Bit x86 calling convention, when I
noticed something.

All of these calling conventions have something in common. If the
return type is a class by value, then the caller function passes the
address of allocated memory to the callee as though it were the first
argument to the callee (and all of the other arguments get moved down
one space). So let's say we start off with a function whose signature
is:

    std::mutex Func(void);

Well, really this gets invoked by the compiler as though it were:

    void Func(std::mutex*);

And so if you want to write a function that returns a locked mutex by
value, all you need to do is:

    void Func_detail(std::mutex *const p)
    {
        ::new(p) std::mutex();
        p->lock();
    }

And then you take the address of this function and you cast it to a
function pointer with a different signature:

    auto Func = reinterpret_cast<std::mutex (*)(void)>(Func_detail);

Invoke this function pointer and it returns a locked mutex by value.
Try it out on GodBolt, it works with all of the compilers, even when
you give them "-m32" to put them in 32-Bit mode (so that they use
cdecl).

    https://godbolt.org/z/P18hzoMhj

I have Linux on my laptop here and I've got cross compilers and
emulators for aarch64 and arm32, so I'll try it with them too just
now.

And here's the GodBolt code copy-pasted:

#include <new> // placement 'new'
#include <iostream> // cout
using std::cout, std::endl;

struct Mutex {
    // ---------- cannot move and cannot copy ----------
    Mutex(Mutex &&) = delete;
    Mutex(Mutex const & ) = delete;
    Mutex &operator=(Mutex &&) = delete;
    Mutex &operator=(Mutex const & ) = delete;

    Mutex(void) { cout << "construct\n"; }
    void lock(void) { cout << "lock\n" ; }
    void unlock(void) { cout << "unlock\n" ; }
    ~Mutex(void) { cout << "destroy\n" ; }
};

void GiveMeLockedMutex_detail(Mutex *const pm, int const a, double
const b, char const *const p)
{
    cout << "Args: " << a << " " << b << " " << p << endl;

    ::new(pm) Mutex();

    pm->lock();
}

auto const GiveMeLockedMutex = reinterpret_cast<Mutex (*)(int, double,
char const *)>(GiveMeLockedMutex_detail);

int main(int const argc, char **const argv)
{
    auto mtx = GiveMeLockedMutex(5,7.8,"monkey");
    mtx.unlock();
}

Received on 2023-07-16 20:22:38