Date: Sun, 25 Feb 2024 14:03:23 +0000
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();
}
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 14:03:25