Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Finally found a use for the `continue` statement.

47 views
Skip to first unread message

Alf P. Steinbach

unread,
Mar 10, 2021, 6:30:15 PM3/10/21
to
I finally found a use for the `continue` statement, after some 25+ years
of C++. Also, learned that (apparently, but haven't really tested)
`std::optional` seems to be `constexpr`-compatible. Code:

inline constexpr auto operator""_u128( const C_str spec )
-> Uint_128
{
const char apostrophe = '\'';

// TODO: use common exception handling.
Uint_128 result = 0;
for( int i = 0; spec[i] != '\0'; ++i ) {
const char ch = spec[i];
if( ch != apostrophe ) {
if( not( '0' <= ch and ch <= '9' ) ) {
throw runtime_error( "Invalid character ‘"s << ch
<< "’ in _u128 literal." );
}
optional<Uint_128> shifted = mul( 10, result );
if( shifted.has_value() ) {
const Uint_128 new_result = shifted.value() + (ch -
'0');
if( new_result >= result ) {
result = new_result;
continue;
}
}
throw runtime_error( "Uint_128 value range exceeded for
_u128 literal." );
}
}
return result;
}

However, it would be a little strange if indeed I had found a case where
there is no arguably better alternative than using `continue`.

So, mainly because I don't see it, is there a better way to do that
failure handling, preferably without repeating oneself?


- Alf

MrSpook...@tmpo953x94x_dv5i2w.ac.uk

unread,
Mar 11, 2021, 4:14:41 AM3/11/21
to
On Thu, 11 Mar 2021 00:30:01 +0100
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
>However, it would be a little strange if indeed I had found a case where
>there is no arguably better alternative than using `continue`.

You've demonstrated many times that your style of coding is overcomplicated
gibberish so no surprise you don't like a simple construct such as continue;

Paavo Helde

unread,
Mar 11, 2021, 4:53:42 PM3/11/21
to
11.03.2021 01:30 Alf P. Steinbach kirjutas:
> I finally found a use for the `continue` statement, after some 25+ years
> of C++.

Good for you. There is nothing wrong with using 'continue' when it fits
the task.

But if you want to make your code better, then with your example it's
simple. You have just move the overflow exception throwing inside the
Uint_128 * and + operators.

This would have the side benefit of getting rid of those optional
values, there is no point in dragging zombies along if you are just
throwing an exception whenever one appears.

The final code might then look something like that:

inline constexpr Uint_128 operator""_u128( const char* spec) {
Uint_128 result = 0;
for (int i = 0; spec[i]; ++i ) {
const char ch = spec[i];
if (ch == '\'') {
continue;
}
if (ch<'0' || ch>'9') {
throw runtime_error( "Invalid character ‘"s << ch << "’ in
_u128 literal." );
}
// this will throw inside on overflow
result = result*10 + (ch - '0');
}
return result;
}

red floyd

unread,
Mar 11, 2021, 5:02:54 PM3/11/21
to
Or, to keep Alf's head from exploding because of the continue...

inline constexpr Uint_128 operator""_u128( const char* spec) {
Uint_128 result = 0;
for (int i = 0; spec[i]; ++i ) {
const char ch = spec[i];
if (ch != '\'') {

Alf P. Steinbach

unread,
Mar 11, 2021, 7:08:03 PM3/11/21
to
Thanks for these attempts.

There are some costs:

* The overflow exception message no longer mentions a literal.

* The arithmetic operations no longer wrap, as usual for unsigned type.

For a compile time exception both Visual C++ and MinGW g++ report the
origin as the literal expression itself, so that's OK anyway. I guess
with some other compiler that reported only the expression throwing
site, moving the throwing down into arithmetic operations could create a
little mystery when this happens. But I don't know such compiler.

Perhaps there are good workarounds for the exception message?

However, I see no practical way to deal with the throwing arithmetic
operations for an unsigned type. Special named throwing arithmetic
operations could be defined for this and similar uses. But thinking
about that my head feels like exploding. :(


Cheers,

- Alf

Paavo Helde

unread,
Mar 12, 2021, 5:42:24 AM3/12/21
to
This increases the indentation of function's main logic by 1 level,
which is a bad thing in my book.

Anyway, I do not like any of the shown variants very much, they remind
me C style too much (or for Alf's variant, Pascal) where 95% of code is
dedicated for error checking and handling. In C++ we can have main logic
clear and visible, with error checking still there, but not taking the
most prominent place. Maybe something like that (you can see I still
prefer 'continue;' over an extra indentation level in main logic):

inline constexpr Uint_128 operator""_u128( const char* spec) {
Uint_128 result = 0;
for (int i = 0; spec[i]; ++i ) {
if (Preprocess(spec[i])) {
continue;
}
// actual work
result = result*10 + (spec[i] - '0');
}
return result;
}

// Range checking, error handling, and trivial preprocessing
// tucked away from the main logic.
inline constexpr bool Preprocess(char ch) {
if (ch == '\'') {
// processed fully
return true;
}
if (ch<'0' || ch>'9') {
throw runtime_error(
"Invalid character ‘"s << ch << "’ in _u128 literal."
);
}
// not processed
return false;
}

Bo Persson

unread,
Mar 12, 2021, 6:17:16 AM3/12/21
to
But *how* bad is an extra indent (for an else-part), compared to a
head-exploding continue? :-)

Almost every use of continue in

if (condition)
continue;

normal_processing;

can of course be written as

if (!condition)
normal_processing;

And very often you can make this even more readable by chosing
"condition" properly, so that the negation in the if-statement can be
avoided.


Bo Persson

Paavo Helde

unread,
Mar 12, 2021, 8:42:44 AM3/12/21
to
In this very simple example where "normal processing" is one line, not
very bad. But in my experience, the "normal processing" block is often
much more complicated than 1 line and will contain its own control
structures, and every bit of help to keep it simpler is welcome.

Also, the software complexity has a tendency to grow exponentially.
Let's say there is some minor annoyance which makes the code 25% more
complex to understand than needed. Now tack 10 such minor annoyances on
top of each other, probably introduced by different developers over
years (who were just trying to not ruin any existing functionality), and
you end up with an increase of complexity not 50% or 100%, but ca 10
times. There would be little hope any human could understand what the
code is now doing, including the original author.

Cheers
Paavo

Tim Rentsch

unread,
Mar 13, 2021, 1:33:43 AM3/13/21
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> I finally found a use for the `continue` statement, after some 25+
> years of C++. Also, learned that (apparently, but haven't really
> tested) `std::optional` seems to be `constexpr`-compatible. Code:

[lightly edited, mainly to avoid line wrapping]
I would probably write something like this:


#include <stdexcept>
using namespace std::literals;
using Uint_128 = __uint128_t;

inline constexpr Uint_128
operator""_u128( const char *input, unsigned long ){
using oops = std::runtime_error;

auto combine = []( Uint_128 r, Uint_128 d ) -> Uint_128 {
const Uint_128 minus_1 = -1, rh = minus_1 / 10, dh = minus_1 % 10;
if( r > rh || r == rh && d > dh ){
throw oops( "Uint_128 value range exceeded for _u128 literal." );
}
return r*10 + d;
};

Uint_128 r = 0;
for( const char *s = input; *s; s++ ){
if( '0' <= *s && *s <= '9' ) r = combine( r, *s - '0' );
else if( *s == '\'' ) { /* allow ' as separator */ }
else throw oops( "Invalid character >"s + *s + "< in _u128 literal." );
}
return r;
}
0 new messages