[Thoughts/Idea] Labeled Loop Variable

114 views
Skip to first unread message

peetp...@gmail.com

unread,
Jul 20, 2017, 9:49:29 PM7/20/17
to ISO C++ Standard - Future Proposals
I would like to get some feedback on these quick thoughts.
The aim is to make the for-loop more flexible (by supporting better loop control-flow) and eventually turning it into an expression.

Also, please excuse my English (It's not my native language).

// Feature: Labeled Loop Variable
//
// Note: The code below is pseudo code and therefore cannot be compiled with
//       current compilers.
//       This isn't a proposal, these are just ideas (thoughts) :).

template <typename T>
void Test(const std::vector<T>& data) {
    //
    // Syntax (Lines marked with + are additions/modifications):
    // -----------------------------------------------------------------------
    //
    // + decl-specifier-mod:
    // +      constexpr
    // +      static
    // +      thread_local
    // +      cv-qualifier-seq opt
    //
    // + labeled-for:
    // +      decl-specifier-mod opt identifier :
    //
    // + labeled-for-default-expression:
    // +      -> init-declarator
    //
    //   iteration-statement:
    // +      for labeled-for opt ( init-statement condition opt ; expression opt ) labeled-for-default-expression opt
    // +      for labeled-for opt ( for-range-declaration : for-range-initializer ) labeled-for-default-expression opt
    //
    // + labeled-break-expression:
    // +      : expr-or-braced-init-list opt
    //
    // + labeled-break-statement-expression:
    // +      identifier labeled-break-expression opt
    // +      expr-or-braced-init-list
    //
    //   jump-statement:
    // +      break labeled-break-statement-expression opt ;
    //        continue ;
    //        return expr-or-braced-init-list opt ;
    //        goto identifier ;
    // -----------------------------------------------------------------------

    // Examples
    // -----------------------------------------------------------------------
    //
    // The below loop will emit a variable definition (labeled loop variable)
    // outside of the for-loop with an optional default initialization.
    //
    // The labeled loop variable cannot be accessed inside the for-loop and
    // can only be indirectly modified by using the 'break' keyword with an
    // expression as operand.
    // This has the advantage that we can use the name of the
    // labeled loop variable as label to break out of that loop.
    //
    // There are several ways one can use the 'break' keyword in this context.
    //
    // 1.)
    //
    // This is emitted before the loop:
    // sugar for -> int calc_loop = 0;

    for calc_loop: (auto& item : data) -> int{0} {
        if (item()) {
            continue;
        }
        break 1;
    }

    if (calc_loop == 0) {
        // The loop did not break
    } else {
        // calc_loop = 1
    }

    // 2.)
    //
    // The labeled loop variable 'exit_code' which type is std::optional<int>
    // is default initialized.
    //
    // This is emitted before the loop:
    // sugar for -> std::optional<int> exit_code;

    for exit_code: (auto& item : data) -> std::optional<int>{} {
        if (item()) {
            continue;
        }
        break 1;
    }

    if (exit_code) {
        auto val = *exit_code;

        // Deal with the error "returned" by the loop
    }

    // 3.)
    //
    // It's possible to break out of an outer labeled for-loop
    // by using its label.
    // One can also specify an additional expression to be assigned
    // to the labeled loop variable associated with the label.

    for loop: (auto& item : data) -> std::optional<int>{} {
        for (auto nested : item) {
            if (nested.cond1) {
                // This is going to break out of the outer loop.
                // Note that this isn't assigning an expression, this works
                // because you can't access the labeled loop variable inside the
                // loop. It can only be used as a label.
                break loop;
            }

            if (nested.special_cond) {
                break loop: nested.val;
            }

            if (nested.super) {
                break loop: 13 + nested.val;
            }
        }
    }

    if (loop) {
        auto error = *loop;
        // Process the error...
    }

    // More thoughts:
    //
    // This whole for-loop-statement can be considered as an expression which
    // returns the labeled loop variable, therefore the explicit loop variable
    // can be omitted.
    //
    // The syntax can be simplified to something like this:

    auto loop = for (auto& item : data) -> std::optional<int>{} {
        for nested_loop: (auto nested : item) {
            if (nested.cond1) {
                break loop;
            }

            if (nested.special_cond) {
                break loop: nested.val;
            }

            if (nested.super) {
                break loop: 13 + nested.val;
            }

            if (nested.naeh) {
                break nested_loop;
            }
        }
    };

    if (loop) {
        // Do the processing...
    }
}

Why these thoughts? Well, I've been thinking of getting myself to know clang better. So the idea is to implement some playful new C++ syntax in clang :P
If this idea seems like a "okay"-idea, I might think of implementing this in clang.

Nicol Bolas

unread,
Jul 20, 2017, 11:52:54 PM7/20/17
to ISO C++ Standard - Future Proposals
The much simpler for loop exit strategies proposal covers the more reasonable cases of this. As for breaking to particular loops and such, no. That's way too much complexity. If your loops are that complicated, maybe you should be using a different construct.

Giovanni Piero Deretta

unread,
Jul 21, 2017, 11:00:38 AM7/21/17
to ISO C++ Standard - Future Proposals
On Friday, July 21, 2017 at 4:52:54 AM UTC+1, Nicol Bolas wrote:
The much simpler for loop exit strategies proposal covers the more reasonable cases of this. As for breaking to particular loops and such, no. That's way too much complexity. If your loops are that complicated, maybe you should be using a different construct.

the op proposal allows returning values from a loop which is nice. Although I can't shake the feeling that just wrapping the loop in a parameterless lambda subsumes both proposals and also is lightweight enough not to require additional sugar:

auto found = [&]
{
   for(auto&& r : v)
         for (auto&&x : r)
              if ( predicate(x) )
                return optional{x};
  return {};
}

Larry Evans

unread,
Jul 22, 2017, 3:47:26 AM7/22/17
to std-pr...@isocpp.org
On 07/21/2017 10:00 AM, Giovanni Piero Deretta wrote:
> On Friday, July 21, 2017 at 4:52:54 AM UTC+1, Nicol Bolas wrote:
>
> The much simpler for loop exit strategies proposa
> <http://wg21.link/P0082>l covers the more reasonable cases of this.
> As for breaking to particular loops and such, no. That's way too
> much complexity. If your loops are that complicated, maybe you
> should be using a different construct.
>
>
> the op proposal allows returning values from a loop which is nice.
> Although I can't shake the feeling that just wrapping the loop in a
> parameterless lambda subsumes both proposals and also is lightweight
> enough not to require additional sugar:
>
> auto found = [&]
> {
> for(auto&& r : v)
> for (auto&&x : r)
> if ( predicate(x) )
> return optional{x};
> return {};
> }

Giovanni, with this scheme, how would you exit a specific
loop. For example, the loop with loop variable x. The
above, IIUC, exits both the r and x loops.

I believe this was done OP's last example, IOW, the one
with:

auto loop = for (auto& item : data) -> std::optional<int>{} {
for nested_loop: (auto nested : item) {
if (nested.cond1) {
break loop;//break's outer loop.
}

if (nested.special_cond) {
break loop: nested.val;//breaks outer loop.
}

if (nested.super) {
break loop: 13 + nested.val;//breaks outer loop.
}

if (nested.naeh) {
break nested_loop;//breaks just inner loop.
}
}
};

if (loop) {
// Do the processing...
}

BTW, peetpaul69, this beaking out of several loops idea was
proposed in 1979 here:

http://dl.acm.org/citation.cfm?id=359057

however, in the arsac paper, instead of:

break labeled-break-statement-expression

the syntax was:

exit(p)

where p is integer >= 0 and the exit(p) statement means:

go out of the p-th enclosing loop and proceed
sequentially after it.

The examples in the reference included restructuring go-to
programs to make them structured.

Just thought this might provide another selling point for
your idea.

-regards,
Larry


Larry Evans

unread,
Jul 22, 2017, 4:01:24 AM7/22/17
to std-pr...@isocpp.org
On 07/22/2017 02:48 AM, Larry Evans wrote:
[snip]
> BTW, peetpaul69, this beaking out of several loops idea was
> proposed in 1979 here:
>
> http://dl.acm.org/citation.cfm?id=359057
>
It was also proposed in this newsgroup in a post with headers:

From: amg...@gmail.com
Newsgroups: gmane.comp.lang.c++.isocpp.proposals
Subject: Have you ever considered break( n ) ?
Date: Thu, 8 Sep 2016 04:54:14 -0700 (PDT)

Several people gave feedback to that post.

-regards,
Larry


Reply all
Reply to author
Forward
0 new messages