C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Custom call convention per type

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Mon, 8 Aug 2022 17:24:25 +0200
pon., 8 sie 2022 o 13:00 Frederick Virchanza Gotham via Std-Proposals
<std-proposals_at_[hidden]> napisaƂ(a):
>
> On Thu, Jul 28, 2022 at 10:26 PM Marcin Jaczewski wrote:
> >
> > Image we add new contextual keyword like:
> > ```
> > class Type arg_destructor_in_callee
> > {
> > };
> > ```
> > Now when the function `void foo(Type arg)` is called, the object `arg`
> > will be destroyed
> > in the body of `foo` not by the caller. This will make for compiler
> > easy to see if object was
> > moved before the destructor call and then if the destructor was inlined
> > then it could remove it all. This will work great with `bitcopies` or
> > `Trivially Relocatable` types.
>
>
> The following program can be toggled to "destroy in caller" or
> "destroy in callee" by setting the global boolean "destroy_in_callee"
> to true or false.
>
> I realise that the following code won't give you the optimisation
> you're looking for, but is it at least the behaviour you're going for?
>
> // Step 1: Have a global boolean to specify destroy by caller/callee
> bool constexpr destroy_in_callee = true;
>
> #include <iostream>
>
> // Step 2 : Write a sample dummy class that says hello and goodbye
> struct MyClass {
>
> bool is_valid = true;
>
> MyClass(char const* = nullptr)
> {
> std::cout << "Constructing object" << std::endl;
> }
>
> MyClass(MyClass &&temp)
> {
> temp.is_valid = false;
> }
>
> ~MyClass(void)
> {
> if ( false == is_valid ) return;
>
> std::cout << "Destroying object" << std::endl;
> }
> };
>
> // Step 3: Write a template function to get an L-value reference to a
> // temporary object
> template <class T>
> T &LvalueRef(T &&arg)
> {
> return arg;
> }
>
> #include <optional>
>
> // Step 4: Write a preprocessor macro to create a temporary
> // object of type 'optional' from a temporary.
> #define ARG(temp) LvalueRef(std::optional<decltype(temp)>( \
> std::move(temp) ))
>
> // Step 5: Write a function that can either do "destroy by caller"
> // or "destroy by callee"
> void SomeFunction(std::optional<MyClass> &arg)
> {
> // I have the choice now whether the string
> // gets destroyed in this function or in
> // the calling function.
>
> if constexpr ( destroy_in_callee ) arg.reset();
> }
>
> int main(void)
> {
> // Step 6: Call the function and check where the temporary is destroyed
>
> SomeFunction( ARG(MyClass(nullptr)) ),
> (std::cout << "Hello again from Main" << std::endl);
>
> std::cout << "Last line in body of Main" << std::endl;
> }
>
>
> This program will print either:
>
> Constructing object
> Hello again from Main
> Destroying object
> Last line in body of Main
>
> or:
>
> Constructing object
> Destroying object
> Hello again from Main
> Last line in body of Main

You missed the whole point for this proposal.
Yes you can currently move point where nested object will be destroyed but
an unneeded destructor still needs to be called after `SomeFunction`
as `main` does not have any knowledge of what happened in that function.
Any number of `if` in `~MyClass(void)` will change that.
Because ABI defines it will happen that way.


As massive ABI break is a very bad thing, I suggest adding a tag to type that
informs ABI to alter default behavior for this type.
No mass breaking as nothing will change unless someone requests it.
And then we have the same situation like when someone adds new members
to some structure.

> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2022-08-08 15:24:38