C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::dummy_prvalue -- Kind of, sort of, like a std::declval that you can evaluate

From: Breno Guimarães <brenorg_at_[hidden]>
Date: Sat, 15 Jul 2023 12:07:22 -0300
I'm on my phone so I can't test, but have you tried returning a union? Not
sure if you could get what you want, but it could work.

Breno G.

Em sáb., 15 de jul. de 2023 10:03, Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> escreveu:

> Let's say I have a function as follows that might throw an exception:
>
> class MyClass { ........ };
>
> MyClass Func(int, double, char*) noexcept(false);
>
> I decide to wrap this function in a function that never throws:
>
> thread_local std::exception_ptr e;
>
> MyClass FuncNoThrow(int a, double b, char *c) noexcept
> {
> try { return Func(a,b,c); }
> catch(...) { e = std::current_exception(); }
>
> return {};
> }
>
> That return statement at the end is really just to suppress a compiler
> warning and also to avoid what strictly-speaking is 'undefined
> behaviour' -- you're not allowed to fall off the end of a
> non-void-returning function.
>
> In the System V x86_64 calling convention, a return statement can do
> three different things depending on how big the return value is:
> (1) If the return value <= 8 bytes, it just sets the RAX register
> (2) If 8 bytes < return value <= 16 bytes, it sets both RAX and RDX
> (3) if return value > 16 bytes, it stores the return value in the
> memory pointed to by RDI
>
> So if you don't have a return statement in a non-void-returning
> function, then there won't be any real drama so long as you don't try
> to access the return value in the caller function (because you're not
> accessing RAX, RDX, or memory pointed to by RDI). Nonetheless, the C++
> Standard says that it's undefined behaviour irrespective of whether
> you try to do anything with the return value. (I can see however see
> how there would be a problem if MyClass had a nontrivial destructor).
>
> FuncNoThrow could be utilised as follows:
>
> void Monkey(void)
> {
> e = nullptr;
> auto retval = FuncNoThrow(5, 6.2, nullptr);
> if ( nullptr != e ) std::rethrow_exception(e);
> cout << retval << endl;
> }
>
> This will all work fine so long as 'MyClass' has a default
> constructor. If it doesn't, then the "return {};" won't compile. If
> there's no default constructor, we could try something like:
>
> MyClass FuncNoThrow(int a, double b, char *c) noexcept
> {
> try { return FunctionName(a,b,c); }
> catch(...) { e = std::current_exception(); }
>
> alignas(MyClass) static char dummy[sizeof(MyClass)] = {};
> return *static_cast<MyClass*>(static_cast<void*>(dummy));
> }
>
> This would work fine unless the 'move constructor' for MyClass gets
> invoked and tries to copy data to a nullptr. What I really want here
> is the mandatory elision of copy/move operations provided by "return
> value optimisation", but unfortunately it's not provided in this case.
> Somehow I need to get a PRvalue in order for elision to be mandatory.
> So maybe something along the lines of:
>
> void dummy_prvalue_detail(void) {}
>
> template<typename T> requires std::is_trivially_destructible_v<
> std::remove_cvref_t<T> >
> std::remove_cvref_t<T> dummy_prvalue(void) noexcept
> {
> typedef std::remove_cvref_t<T> TT;
> TT (*const funcptr)(void) = reinterpret_cast<TT(*)(void)>(
> dummy_prvalue_detail );
> return funcptr(); // guaranteed elision of move/copy operations
> here
> }
>
> What I really need here is something along the lines of 'std::declval' but:
> (1) It can be evaluated
> (2) It gives you a PRvalue (ensuring elision for return value optimisation)
> (3) It is safe and its behaviour is well-defined so long as you don't
> try to access the non-existent object
> (4) It will reject types that have a non-trivial destructor
>
> An alternative to this would be to allow non-void-returning functions
> to just fall off the end, so long as the return value is not used in
> the caller function, meaning we'd just have:
>
> MyClass FuncNoThrow(int a, double b, char *c) noexcept
> {
> try { return Func(a,b,c); }
> catch(...) { e = std::current_exception(); }
>
> // falling off the end here is fine so long
> // as MyClass has a trivial destructor, and
> // so long as you don't use the return value
> }
>
> So maybe C++26 could either:
> (a) Allow the falling off the end of non-void-returning functions so
> long as the return type is trivially destructible.
> or:
> (b) Have in the standard library a "dummy_prvalue" template function
> such as what I've written, with well-defined behaviour so long as you
> don't try to access the return value.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2023-07-15 15:07:35