Date: Sun, 25 Feb 2024 15:24:26 +0000
If I ever saw such a pattern in production code I would be 99.9% that it was bugged even without looking at what else it is doing.
Clean up code should never fail. This is not to say that such could never happen, a noticeable example are windows sockets, but the way you handle those cases is to either delegate the destruction to something else that tries again later, or you change the conditions to make sure it would succeed. It never happens that you fail to do clean up and you can just continue along as if nothing ever happened, if you could you wouldn't do it to begin with.
That's my 2cents
________________________________
From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]>
Sent: Sunday, February 25, 2024 3:03:23 PM
To: std-proposals <std-proposals_at_[hidden]>
Cc: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Subject: [std-proposals] Bulldoze Exceptions
Let's say you have cleanup code inside a function, something like:
void Func(void *const p, int const arg)
{
auto cleanup = [p,arg]()
{
CleanA();
CleanB();
CleanC();
};
Auto( cleanup() );
// Do stuff that hopefully works
}
The cleanup code might throw an exception, and if it does, you want it to keep bulldozing forward with the cleanup, so you do:
void Func(void *const p, int const arg)
{
auto cleanup = [p,arg]()
{
try { CleanA(); } catch (...){}
try { CleanB(); } catch (...){}
try { CleanC(); } catch (...){}
};
Auto( cleanup() );
// Do stuff that hopefully works
}
I was thinking . . . what if we could mark a block of code as 'bulldoze', meaning that it catches and discards all exceptions. So the lambda becomes:
auto cleanup = [p,arg]()
{ _Bulldoze:
CleanA();
CleanB();
CleanC();
};
Of course there would have to be some restrictions inside a block marked as bulldoze. For example consider:
{ _Bulldoze:
stringstream ss;
ss << 77;
cout << std::move(ss).str();
}
The problem with this block of code is that if the first line throws, then we don't have a variable named 'ss' and so the second line is invalid. So if you define a variable inside a bulldozer block, then there are two rules:
Rule No. 1: The variable must be an intrinsic type, or it must be a class being constructed with a 'noexcept' constructor.
Rule No. 2: The expressions which are required to be evaluated in order to construct the variable must all be 'noexcept'.
So the following is valid:
{ _Bulldoze:
int const i = SomeNoExceptFunc();
cout << i;
}
But the following is invalid:
{ _Bulldoze:
int const i = SomeThrowingFunc();
cout << i;
}
If you absolutely must create a variable of class type that might throw, then use std::optional:
{ _Bulldoze:
optional<stringstream> ss;
if ( ss ) *ss << 77;
if ( ss ) cout << std::move(*ss).str();
}
Clean up code should never fail. This is not to say that such could never happen, a noticeable example are windows sockets, but the way you handle those cases is to either delegate the destruction to something else that tries again later, or you change the conditions to make sure it would succeed. It never happens that you fail to do clean up and you can just continue along as if nothing ever happened, if you could you wouldn't do it to begin with.
That's my 2cents
________________________________
From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]>
Sent: Sunday, February 25, 2024 3:03:23 PM
To: std-proposals <std-proposals_at_[hidden]>
Cc: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Subject: [std-proposals] Bulldoze Exceptions
Let's say you have cleanup code inside a function, something like:
void Func(void *const p, int const arg)
{
auto cleanup = [p,arg]()
{
CleanA();
CleanB();
CleanC();
};
Auto( cleanup() );
// Do stuff that hopefully works
}
The cleanup code might throw an exception, and if it does, you want it to keep bulldozing forward with the cleanup, so you do:
void Func(void *const p, int const arg)
{
auto cleanup = [p,arg]()
{
try { CleanA(); } catch (...){}
try { CleanB(); } catch (...){}
try { CleanC(); } catch (...){}
};
Auto( cleanup() );
// Do stuff that hopefully works
}
I was thinking . . . what if we could mark a block of code as 'bulldoze', meaning that it catches and discards all exceptions. So the lambda becomes:
auto cleanup = [p,arg]()
{ _Bulldoze:
CleanA();
CleanB();
CleanC();
};
Of course there would have to be some restrictions inside a block marked as bulldoze. For example consider:
{ _Bulldoze:
stringstream ss;
ss << 77;
cout << std::move(ss).str();
}
The problem with this block of code is that if the first line throws, then we don't have a variable named 'ss' and so the second line is invalid. So if you define a variable inside a bulldozer block, then there are two rules:
Rule No. 1: The variable must be an intrinsic type, or it must be a class being constructed with a 'noexcept' constructor.
Rule No. 2: The expressions which are required to be evaluated in order to construct the variable must all be 'noexcept'.
So the following is valid:
{ _Bulldoze:
int const i = SomeNoExceptFunc();
cout << i;
}
But the following is invalid:
{ _Bulldoze:
int const i = SomeThrowingFunc();
cout << i;
}
If you absolutely must create a variable of class type that might throw, then use std::optional:
{ _Bulldoze:
optional<stringstream> ss;
if ( ss ) *ss << 77;
if ( ss ) cout << std::move(*ss).str();
}
Received on 2024-02-25 15:24:29