Topic: Compile-time check for noexcept: a thought of
Author: David Krauss <potswa@gmail.com>
Date: Sat, 04 Jan 2014 13:24:22 +0800
Raw View
On 1/4/14 12:35 PM, Kazutoshi Satoda wrote:
> "static_assert(noexcept(expr))" is a way and may help. But it is hard
> to maintain in sync with the real code including variable
> initialization and destruction. Compilers can know precisely which
> expressions (function calls) should be tested.
For expression-statements, you can use a macro:
#define do_noexcept( EXPR ) \
do { \
static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
EXPR; \
while(0)
void some_class::strong_op()
{
some_class transitive(*this);
transitive.op1_may_throw();
transitive.op2_may_throw();
do_noexcept( *this = std::move(transitive) );
}
If we wanted a similar construct to work on an entire function body, I
would suggest syntax like a noexcept(auto) exception-specification to
analyze all the potentially-evaluated expressions. Implicitly declared
destructors already do this by default, although the analysis is
inherently limited. The construct could be applied to a sequence of
statements using a lambda expression.
auto move_finish = [&]() noexcept(auto) {
*this = std::move(transitive);
};
static_assert( noexcept( move_finish() ), "Transitive move may throw." );
move_finish();
}
However, this cannot assert that initialization of an object may not
throw but its use may throw. I'm not sure if that's a useful case though.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Kazutoshi Satoda <k_satoda@f2.dion.ne.jp>
Date: Tue, 07 Jan 2014 11:33:27 +0900
Raw View
On 2014/01/04 14:24 +0900, David Krauss wrote:
> On 1/4/14 12:35 PM, Kazutoshi Satoda wrote:
>> "static_assert(noexcept(expr))" is a way and may help. But it is hard
>> to maintain in sync with the real code including variable
>> initialization and destruction. Compilers can know precisely which
>> expressions (function calls) should be tested.
>=20
> For expression-statements, you can use a macro:
>=20
> #define do_noexcept( EXPR ) \
> do { \
> static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
> EXPR; \
> while(0)
....
That's an option for the current standard. But, as you know, it can't be
applied straight to variable initialization and destruction.
> If we wanted a similar construct to work on an entire function body, I=20
> would suggest syntax like a noexcept(auto) exception-specification to=20
> analyze all the potentially-evaluated expressions. Implicitly declared=20
> destructors already do this by default, although the analysis is=20
> inherently limited. The construct could be applied to a sequence of=20
> statements using a lambda expression.
>=20
> auto move_finish =3D [&]() noexcept(auto) {
> *this =3D std::move(transitive);
> };
> static_assert( noexcept( move_finish() ), "Transitive move may thr=
ow." );
> move_finish();
> }
>
> However, this cannot assert that initialization of an object may not=20
> throw but its use may throw. I'm not sure if that's a useful case though.
It has the same problem that "noexcept block" in original noexcept
proposal had, with more syntactic overhead.
http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/
(a quote from the list of reasons to drop static check of noexcept from
the original proposal which had "noexcept block" to disable the check
for a region of code in a function)
> 2. The noexcept block for locally disabling the static check was not
> good enough for all situations, for instance it was not suitable
> for disabling the check in constructors=92 initialization lists.
> 3. Requiring an additional block in function code was too
> inconvenient. Additional scope could require rethinking the scopes
> of local variables and unnecessarily make the functions longer. ...
Such a requirement of additional block (another scope) is the cause of
limitation about variable initialization and destruction, and is what
the label approach is trying to bypass.
--=20
k_satoda
--=20
---=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.
.
Author: Matthew Woehlke <mw_triad@users.sourceforge.net>
Date: Tue, 07 Jan 2014 10:20:32 -0500
Raw View
On 2014-01-06 21:33, Kazutoshi Satoda wrote:
> On 2014/01/04 14:24 +0900, David Krauss wrote:
>> For expression-statements, you can use a macro:
>>
>> #define do_noexcept( EXPR ) \
>> do { \
>> static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
>> EXPR; \
>> while(0)
> ...
>
> That's an option for the current standard. But, as you know, it can't be
> applied straight to variable initialization and destruction.
What about [[noexcept]]?
// initializer list may not throw
foo::foo() : [[noexcept]] ...
{
...
[[noexcept]] {
// No statement in this scope may throw
...
}
...
a = [[noexcept]] b; // assignment may not throw
}
Not sure if that's what was meant about assignment, or if that would be
the best syntax. A possible advantage though is that other variations of
the above theme can be added as deemed necessary.
>> 3. Requiring an additional block in function code was too
>> inconvenient. Additional scope could require rethinking the scopes
>> of local variables and unnecessarily make the functions longer. ...
>
> Such a requirement of additional block (another scope) is the cause of
> limitation about variable initialization and destruction, and is what
> the label approach is trying to bypass.
I don't see that the label approach is any different. Since you
specified that the effect ends at the end of scope, how is this:
{
...
noexcept:
...
}
....any different from this?
{
...
[[noexcept]] {
...
}
}
If one really needs to 'undo' noexcept, i.e. use declarations from
within the noexcept scope in the next scope, what about allowing
'inline' to be used on a scope to extend the lifetime of objects in the
scope to the parent scope (a bit like e.g. Python)?
IOW:
int bar() {
inline [[noexcept]] {
double a = 42.0;
}
// 'a' is still in scope here
}
Obviously, this would not allow extending from e.g. a function scope to
global scope (and 'inline' means something different there anyway).
--
Matthew
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.
Author: Kazutoshi Satoda <k_satoda@f2.dion.ne.jp>
Date: Mon, 13 Jan 2014 13:15:23 +0900
Raw View
On 2014/01/08 0:20 +0900, Matthew Woehlke wrote:
> On 2014-01-06 21:33, Kazutoshi Satoda wrote:
>> On 2014/01/04 14:24 +0900, David Krauss wrote:
>>> For expression-statements, you can use a macro:
>>>
>>> #define do_noexcept( EXPR ) \
>>> do { \
>>> static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
>>> EXPR; \
>>> while(0)
>> ...
>>
>> That's an option for the current standard. But, as you know, it can't be
>> applied straight to variable initialization and destruction.
>
> What about [[noexcept]]?
I think attribute can also be used in place of label to achieve similar
effects with less changes in syntax.
void some_class::strong_op()
{
some_class transitive(*this);
transitive.op1_may_throw();
transitive.op2_may_throw();
[[noexcept]]; // attribute-declaration in 7 [dcl.dcl]
*this = std::move(transitive);
}
I choose label as it looks more intuitive to represent "the critical
line", which divide a function (or a block) into separated parts.
> // initializer list may not throw
> foo::foo() : [[noexcept]] ...
> {
> ...
> [[noexcept]] {
> // No statement in this scope may throw
> ...
> }
> ...
> a = [[noexcept]] b; // assignment may not throw
> }
>
> Not sure if that's what was meant about assignment, or if that would be
> the best syntax. A possible advantage though is that other variations of
> the above theme can be added as deemed necessary.
For mem-initializer-list, it may look appertaining to a specific base or
member. That's why I put comma between "static noexcept" and "Base{}" in
the first post, knowing it looks somewhat odd.
For statements, the use of additional block looks unnecessary and I
don't want that, as discussed below.
For assignment, "[[noexcept]] a = b;" looks more intuitive for me and
also matches the current syntax.
>> Such a requirement of additional block (another scope) is the cause of
>> limitation about variable initialization and destruction, and is what
>> the label approach is trying to bypass.
>
> I don't see that the label approach is any different. Since you
> specified that the effect ends at the end of scope, how is this:
>
> {
> ...
> noexcept:
> ...
> }
>
> ...any different from this?
>
> {
> ...
> [[noexcept]] {
> ...
> }
> }
If there are some local variables above the noexcept line, destructors
of such variables should be checked to be noexcept.
With the requirement of block, you can't mark initialization of a local
variable which is used later in different noexcept requirement.
void foo() noexcept
{
static noexcept:
...
inline noexcept: // temporarily disable static checking
X x{...}; // known to not throw, but not declared with noexcept.
static noexcept: // enable static checking again
... // can use x here
}
And as I basically add an indent for each additional block, I don't
want to make and see accompanying change of indent to just move the
noexcept line up or down.
> If one really needs to 'undo' noexcept, i.e. use declarations from
> within the noexcept scope in the next scope, what about allowing
> 'inline' to be used on a scope to extend the lifetime of objects in the
> scope to the parent scope (a bit like e.g. Python)?
>
> IOW:
>
> int bar() {
> inline [[noexcept]] {
> double a = 42.0;
> }
> // 'a' is still in scope here
> }
It will incur misreading about object lifetime, and probably doesn't
match well with the current syntax and rule about blocks.
--
k_satoda
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
.