for ... else

367 views
Skip to first unread message

lorro (lorro@lorro.hu)

unread,
Jul 17, 2016, 10:38:12 AM7/17/16
to ISO C++ Standard - Future Proposals
Many cases, specific handling is required when the body of the for loop was not executed. These include variable initializations which would've gotten value inside the loop, or throwing exceptions. Currently, these either need double-checking, which is inefficient, or copying the condition, which is error-prone, like:

{
    init;
    if(cond)
        do
        {
            body
            incr;
        } while(cond)
    else
        body-not-executed
}

Note that this is still not exactly what we wanted as variables declared in init are still visible in else branch. Any alternative requires double-checking or introducing a boolean variable that's updated in init and incr. What I'd recommend instead is allowing an else after for:

for(init; cond; incr)
    body
else
    body-not-executed

or,

for( init : range )
    body
else
    body-not-executed

As expected, body-not-executed should run iff cond has failed for the first time (or range was empty); in other words, if body was not executed. Optimizing compilers can unroll this easily and generate optimal code. Also, it does not clash with other language features as else is currently only allowed after an if.
What do you think?

Jared Grubb

unread,
Jul 17, 2016, 10:49:15 AM7/17/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com

Python has a construct like what you describe, but the 'else' body is executed if the range is empty or [the difference] if the range is iterated to exhaustion; in other words, the 'else' clause is executed unless an instance of the loop calls 'break'.

I personally really like this construct in Python because it makes some constructs easier to write. However, it seems that most people find it more confusing than it's worth and wish it had been named something different.

However, I will point out that your proposal would break existing code; for example:

if (cond)
  for (...) {}
else // does this else match the for or if?
  bar();

D. B.

unread,
Jul 17, 2016, 10:50:18 AM7/17/16
to std-pr...@isocpp.org
If this could possibly exist, it would be nice to have it for while, too. Yes, I know for can do the same things as while, but for the same reason, what's one more bit of redundancy going to hurt?

On the converse, it seems that for C++17, while adding arbitrary variable declarations in if and switch, for some reason this was skipped for while - perhaps because of the aforesaid redundancy with for... and/or because it might create awkward questions about do

Also, great point from Jared. In that case you would need alternate syntax, such as for else. It might get too complicated quickly...

szollos...@gmail.com

unread,
Jul 17, 2016, 12:07:42 PM7/17/16
to ISO C++ Standard - Future Proposals
Good point - while should also have this.

szollos...@gmail.com

unread,
Jul 17, 2016, 12:40:27 PM7/17/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
You're perfectly right - in fact, I didn't think about this, sorry. Breaking existing code is a no-go, so we've got three options:
  • define a new keyword (elsefor? when?), which is rarely done;
  • overload another keyword (catch?), this time using a syntax that allows the compiler to tell the difference between a for-catch and a try-catch;
  • specifically resolve this by disallowing for loop having else branch when it's the only command in a non-bracketed if. Or, equivalently, having if-else more 'vexing' than for-else. This is somewhat more complicated to specify but resolves the ambiguity.

As for the break-based else, it solves a somewhat different issue, but that might also be useful. In fact, I like that idea very much (the way we work around it, i.e., what it'd save us, is a lambda - let alone function - that returns inside the loop and has fallback logic after the loop) - it's far from confusing, esp. in C++, when you're used to try-catch blocks. Perhaps a 'when(break 0) {}` and `when(0) {}` (or catch similarly) syntax could handle both. This is very similar to exceptions but it's way faster - in fact, given proper compiler support and inlining, it requires no additional instructions executed, just the jump addresses changed. In fact, there's no reason why we should restrict ourselves to only one of these.


To summarize, we might come up with:

for(init; cond; incr)
    body
catch(break[0])
    body-no-break-executed

catch([0])
    body-not-executed

and, similarly,

for( init : range )
    body
catch(break[0])
    body-no-break-executed
catch([0])
   
body-not-executed

What do you think?

D. B.

unread,
Jul 17, 2016, 1:02:53 PM7/17/16
to std-pr...@isocpp.org
I think using catch here is a terrible idea given its current semantics, and probably not grammatically viable anyway. Insisting on that keyword is liable to sink this proposal before it could ever get anywhere.

Why not just for (...) { /* things if condition == true */ } for else { /* things if condition == false */ }

Ville Voutilainen

unread,
Jul 17, 2016, 1:03:13 PM7/17/16
to ISO C++ Standard - Future Proposals
On 17 July 2016 at 17:38, lorro (lo...@lorro.hu)
<szollos...@gmail.com> wrote:
> Many cases, specific handling is required when the body of the for loop was
> not executed. These include variable initializations which would've gotten

See http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0082r1.pdf

That paper didn't gain consensus in Jacksonville. Different approaches
might come up for C++20.

szollos...@gmail.com

unread,
Jul 17, 2016, 1:04:58 PM7/17/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
Merging with D. B.'s recommendations:


To summarize, we might come up with:

for(init; cond; incr)
    body
catch(break[0])
    body-no-break-executed

catch([0])
    body-not-executed

for( init : range )
    body
catch(break[0])
    body-no-break-executed
catch([0])
   
body-not-executed

while(cond)

    body
catch(break[0])
    body-no-break-executed

catch([0])
    body-not-executed

D. B.

unread,
Jul 17, 2016, 1:14:15 PM7/17/16
to std-pr...@isocpp.org
I do like these, but I find it odd that catch seems to be people's default keyword. Out of interest - is the general reaction to seeing catch used in a non-exception scenario as averse as my own? Heh.


Tony V E

unread,
Jul 17, 2016, 1:19:35 PM7/17/16
to D. B.
I have my doubts about whether the proposal is useful enough to spend time on,
But I like 'for else' better than catch. 

But even better would be 'or else'‎ :-)


Sent from my BlackBerry portable Babbage Device
From: D. B.
Sent: Sunday, July 17, 2016 1:02 PM
Subject: Re: [std-proposals] Re: for ... else

I think using catch here is a terrible idea given its current semantics, and probably not grammatically viable anyway. Insisting on that keyword is liable to sink this proposal before it could ever get anywhere.

Why not just for (...) { /* things if condition == true */ } for else { /* things if condition == false */ }

--
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-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CACGiwhGKv4aNgsNb9%3DOxJYU%3D-4LGwQeFxJ-6N5saezyUPRovyQ%40mail.gmail.com.

D. B.

unread,
Jul 17, 2016, 1:24:24 PM7/17/16
to std-pr...@isocpp.org
On Sun, Jul 17, 2016 at 6:19 PM, Tony V E <tvan...@gmail.com> wrote:
I have my doubts about whether the proposal is useful enough to spend time on,
But I like 'for else' better than catch. 

But even better would be 'or else'‎ :-)

I dunno, it does touch on break-out cases whose verbosity has annoyed me in the past - while being nowhere near the complexity of some examples in that paper - and it looks very logical, chosen labels notwithstanding.

"or else" is funny, but I think for ultimate clarity, it should be "xor else" ;-)


szollos...@gmail.com

unread,
Jul 17, 2016, 1:24:49 PM7/17/16
to ISO C++ Standard - Future Proposals
Actual syntax is not that important to me. For me, for(...) { ... } for break { ... } for else { ... } is perfectly fine, same for while.

D. B.

unread,
Jul 17, 2016, 1:30:05 PM7/17/16
to std-pr...@isocpp.org

Yeah, {for/while} {break/else} definitely get my vote for the moment.

Btw, would you want this to apply to do...while at all? I initially handwaved that away, but it seems like it might also benefit... if anyone has a use for the below pattern:

    do {
        Stuff();
    } while (keep_going)
    do else {
        IfStuffWasOnlyDoneOnce();
    }
    do break {
        IfStuffWasOnlyHalfDone();
    }

Seems less likely, but a logical extension of the other 2, if they were to go anywhere.

D. B.

unread,
Jul 17, 2016, 1:30:57 PM7/17/16
to std-pr...@isocpp.org
and since I apparently can't take my own advice given in the same post, those should be while else and while break

szollos...@gmail.com

unread,
Jul 17, 2016, 1:32:19 PM7/17/16
to ISO C++ Standard - Future Proposals
Thanks! This is very promising, however, I miss the `catch empty` branch. With that, our syntax would look like: {for|while}(...) { ... } catch default ... catch break ... catch() ...

I leave it up for your votes whether we should use:
  • catch
  • else, {for|while} else
  • case
  • when (or any new keyword)

D. B.

unread,
Jul 17, 2016, 1:43:47 PM7/17/16
to std-pr...@isocpp.org
On Sun, Jul 17, 2016 at 6:32 PM, <szollos...@gmail.com> wrote:
Thanks! This is very promising, however, I miss the `catch empty` branch. With that, our syntax would look like: {for|while}(...) { ... } catch default ... catch break ... catch() ...

I leave it up for your votes whether we should use:
  • catch
  • else, {for|while} else
  • case
  • when (or any new keyword)
Labels of false (condition failed), break, and default (all iterations ran fully) are actually mildly appealing.

case as the label-defining keyword seemed nice too, but I such labels would be wholly ambiguous if said loops are contained within a switch block. This probably makes case infeasible.
 
for/while as the keyword seems nice and isn't really confusing. but hey - register is "unused and reserved" as of C++17, and works well as a verb ;-)

szollos...@gmail.com

unread,
Jul 17, 2016, 1:47:14 PM7/17/16
to ISO C++ Standard - Future Proposals
  • Another proposal (that I also would like to see) is to have range schema for std::optional<>. In that case, you could do: for( auto& val : getVal() ) { return handle(val); } for else { return error(); }. Notice that the optional didn't explicitly appear anywhere, so it might very well get optimized away.
  • Any loop that tries to find an element - and then do something to it before returning - could be easier written using for break
  • In case when the loop condition is expensive or alters state (thus cannot be repeated). we currently need to introduce a new bool variable. Two if you want early exit. These are both unneeded cost (additional if executed) and maintenance issues (as it's easy to miss the bool).
  • Some search-like and accumulate-like loops might be way easier written this way.
  • Zero elements in a container is sometimes different and requires special handling. E.g. when the constructor sees that there are zero elements, it might as well not allocate anything and just assign nullptr to a buffer. I understand that this is just a minor optimization.

szollos...@gmail.com

unread,
Jul 17, 2016, 1:58:37 PM7/17/16
to ISO C++ Standard - Future Proposals
Sounds logical to include them as well...

Derek Ross

unread,
Jul 17, 2016, 3:00:37 PM7/17/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
Once the "if initializers" appear in C++17, you could almost get what you want through a macro:


#define if_for(init,cond,incr) if(init ; cond)for(;cond;incr)
...
  if_for
(int i = 0 ; i < n ; i ++)
 
{
   
...etc...
 
}else{
    cout
<< "cond was false\n";
 
}


Hmmm... maybe you could use "if for" as the syntax, which AFAIK is currently illegal in C/C++.

----
Cheers,
Derek

szollos...@gmail.com

unread,
Jul 18, 2016, 3:30:35 AM7/18/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
This evaluates cond twice. Albeit, with double do-while, that's feasible:

if(init ; cond) do { do body while(false); incr; } while(cond);

still not the most beautiful syntax...


---

Another thing, while we are here, that I miss is 'first-round initializer': that is, an initializer that's run after cond is checked. There are situations when the value you wish to initialize a variable to depends on the first value produced by the loop and no default constructor is available (or can be expected if it's a template type). If, furthermore, your range is such that you cannot 'go back' (i.e., iterator::operator++() invalidates and deallocates the original iterator and it's value), and/or checking loop condition is very expensive, you might suddenly find yourself using std::optional<> (or pointer to a heap-allocated object) and checking in each iteration.
Consider this max algorithm instead:

template<typename Range>
auto max(const Range& range) // possibly Range&& as well
{
    for( auto v : range; auto m = v )
        if( m < v ) m=v;
    for default               // use whichever syntax you prefer
        return std::make_optional( m );
    for else
        return std::nullopt;
}

This also shows the requirement that any variables declared in for should be available in for else and for default.

A completely generic syntax could have the following elements (let me know if any further), out of which we might choose which ones to support:

- (precondition: this is handled by an initial if very well, probably no additional support needed)
- (pre-fail: this is handled by an initial if-else, probably no additional support needed)
- either:
    - init: this is the first parameter of the for(;;) syntax
    - head-cond: this is the second parameter of the for(;;) syntax
    - incr: this is the third parameter of the for(;;) syntax
    - postinit: this could be the 4th parameter of the above syntax, like for(;;;)
  or
    - range-init: this is the current for( : ) syntax
    - postinit: this could be the second parameter of the above syntax, like for(;;;)
-
body, which we have now
    - continue statement(s) in body, which execute incr, then body iff tail-cond && head-cond.
    - break statement(s) in body, which go to tail-break
- tail-cond: this is what we have with do { } while( ), in a generic (in theory, not necessarily in practice) framework these can be united
- tails: zero or more distinct element of:
    - tail-break: executed when the loop finished due to break
    - tail-default: executed when we successfully entered at least once to the body of the loop (i.e., head-cond was true at least once) and there were no exceptions
    - tail-else: executed when head-cond was false for the first time
    - (tail-catch: this is handled very well by try-catch, probably no additional support needed)

What do you think and how would you name each of these elements? Did I miss something, is there a better definition to come up with, should any points be refined?

Thanks,
-lorro

szollos...@gmail.com

unread,
Jul 18, 2016, 3:48:01 AM7/18/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
'This also shows the requirement that any variables declared in for should be available in for else and for default.'

Let me rephrase this, it holds for default and break, but certainly not for else.

szollos...@gmail.com

unread,
Jul 23, 2016, 5:57:04 PM7/23/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
One more, somewhat orthogonal thing: do you think we could also consider for( auto& x : std::tuple<...>(...) ) with this proposal, or should that be a separate one?

D. B.

unread,
Jul 23, 2016, 6:04:40 PM7/23/16
to std-pr...@isocpp.org
That sounds completely unrelated. But I guess I should check whether my inferences are accurate. What would it do? act like a template by creating a different loop body for each type of element, where x has a different type each time? I think that would need some different syntax, e.g. "template<T> for (T&& x: tuple)", as just using the same syntax wouldn't make it clear enough that the type changes each time.

D. B.

unread,
Jul 23, 2016, 6:06:34 PM7/23/16
to std-pr...@isocpp.org
...but then (and again, this assumes I'm inferring its purpose correctly) I don't know whether that pattern is common enough to be considered for a syntax extension, nor whether the existing ways to roll-your-own via variadic templates are difficult enough to justify it.

szollos...@gmail.com

unread,
Jul 23, 2016, 6:19:18 PM7/23/16
to ISO C++ Standard - Future Proposals
tuple-for is asked by novice-to-medium-level programmers all the time. Yes, it's a templated body. I didn't write template<typename T> for( T ... ) as we have generic lambdas now, but I'm okay with that syntax as well. I'm aware of the 'templated for_each pattern', but I find it unnecessarily complicated (to explain, mainly) for such a trivial task. Also, I'm hoping to see 'heterogeneous container' visitors (think of boost::fusion) and variant visitors (similarly to for over optional).
Or am I missing something trivial that can already do these, perhaps with some library code?

Nicol Bolas

unread,
Jul 23, 2016, 7:00:19 PM7/23/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
On Saturday, July 23, 2016 at 6:19:18 PM UTC-4, szollos...@gmail.com wrote:
tuple-for is asked by novice-to-medium-level programmers all the time. Yes, it's a templated body. I didn't write template<typename T> for( T ... ) as we have generic lambdas now, but I'm okay with that syntax as well. I'm aware of the 'templated for_each pattern', but I find it unnecessarily complicated (to explain, mainly) for such a trivial task. Also, I'm hoping to see 'heterogeneous container' visitors (think of boost::fusion) and variant visitors (similarly to for over optional).
Or am I missing something trivial that can already do these, perhaps with some library code?

The fact of the matter is, I don't want to see tuple-for. Or language-level visitation. I would much rather see that feature that we discussed earlier that would allow you to transform a tuple (or tuple-like type) into a parameter pack, which you could then unpack like any other.

That'd be far more useful than a mere tuple-for. And a lot easier to standardize.

But as previously pointed out, this is all way off-topic here.

2016. július 24., vasárnap 0:06:34 UTC+2 időpontban D. B. a következőt írta:
...but then (and again, this assumes I'm inferring its purpose correctly) I don't know whether that pattern is common enough to be considered for a syntax extension, nor whether the existing ways to roll-your-own via variadic templates are difficult enough to justify it.

To be fair, it's not easy to write code that will be executed for each element in a tuple. It represents a hole in doing things with tuples.

It's easy to build a tuple. It's easy to pass tuples around. It's easy to get a single element from a tuple. These basic uses of a tuple do not require complex metaprogramming techniques.

But to perform an act, even if it's just a single function call, on each element requires metaprogramming. Granted, performing an act for each member of a struct requires... well, it requires macros & metaprogramming, so it's much worse. But with a tuple, it seems (from the perspective of a novice) to be such a simple thing.

Hence my desire for tuple-to-pack transformations. That's a simple thing that they can understand.

D. B.

unread,
Jul 23, 2016, 7:12:57 PM7/23/16
to std-pr...@isocpp.org
On Sun, Jul 24, 2016 at 12:00 AM, Nicol Bolas <jmck...@gmail.com> wrote:
To be fair, it's not easy to write code that will be executed for each element in a tuple. It represents a hole in doing things with tuples.

It's easy to build a tuple. It's easy to pass tuples around. It's easy to get a single element from a tuple. These basic uses of a tuple do not require complex metaprogramming techniques.

But to perform an act, even if it's just a single function call, on each element requires metaprogramming. Granted, performing an act for each member of a struct requires... well, it requires macros & metaprogramming, so it's much worse. But with a tuple, it seems (from the perspective of a novice) to be such a simple thing.

Hence my desire for tuple-to-pack transformations. That's a simple thing that they can understand.

Yeah, that sounds preferable, especially by using existing (if, at least from what I've seen, a bit underused) syntax, rather than requiring another new set, which would be redundant.

Maybe I was thinking of something else when I implied it wasn't that difficult... a  quick scan shows it looks rather thorny.

BUT! This looks promising if accurate (not tested it myself): http://stackoverflow.com/a/37100197/2757035
Sure, not an immediately intuitive syntax, but makes sense after a few seconds, and way better than the alternatives anyhow.

szollos...@gmail.com

unread,
Jul 25, 2016, 10:17:04 AM7/25/16
to ISO C++ Standard - Future Proposals
Ok, so perhaps we can set aside tuple_for for this one. (I'd also like to see better tuple <-> params & init list interwork, but that deserves a separate, longer thread).
Do you think there's anything to add to the for & while before we make a draft?
Also - do you perhaps have any thoughts on why the previous draft on for didn't make it and how to improve so that this one does?

Thanks,
-lorro

szollos...@gmail.com

unread,
Sep 7, 2016, 2:46:04 PM9/7/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
Hi,

(still working on this...)
Let's revisit visitation. How would you feel about:

std::tuple<int, char, double> nums;
for (auto& v : nums)
{
    v([](auto n) {
        std::cout << n;
    }
}

? That is, define std::tuple<..>::begin() and end() returning visiting iterators (iterators that have operator() that calls the parameter with the corresponding element). This doesn't require any syntax change (albeit compiler support is still desirable to eliminate type switch / virtuals) and we could tick this item on the list. As for the rest, I'm generalizing it a bit..

Thanks,
-lorro

szollos...@gmail.com

unread,
Oct 9, 2016, 10:49:11 AM10/9/16
to ISO C++ Standard - Future Proposals, szollos...@gmail.com
Hi,

tl;dr: this is provided w/o additional language support:
void function(std::vector<int> v)
{
   
for (int j = 0; j < 2; ++j) break_label(abc) {
// Choose exactly one of these by uncommenting:
//      int i; for_if_nodecl(i : v)  { // C++98
//      int i; for_if(i : v)         { // C++11
        for_if
(auto&& i : v)         { // C++11
            std
::cout << " " << i;
           
if (2 < i && i < 5) { break; }
       
}
       
else if ( _empty )           { std::cout << " empty"; }
       
else if (!_break && _broken) { std::cout << " last";  }
       
else if (!_break &&!_broken) { std::cout << " done";  }
       
else if ( _break ) {
            std
::cout << " (break at " << *_begin
                     
<< ", broken: " << _broken << ") ";
           
if (*_begin > 3) {
                std
::cout << std::endl;
                break_to
(abc);
           
}
            continue_from_break
;
       
}
        std
::cout << std::endl;
   
}
}


I had some time to work on this; I've made a macro-based implementation.
/* Why macro? Of course, the expected result is a language feature in the next standard; however, that could use some input from actual use cases. The time it takes to roll out - not just for compilers, but to companies - the build env. supporting the new standard might justify the idea of a transition / backport package. Some of these macros are C++98, which might bridge a 20-year gap. Also, a macro can be undefined at the end of the code parts using it :). */
Interestingly, newer gcc versions optimize this quite well. Expect about 2x - 3x the number of instructions than a plain old loop. Naturally, this is currently not for tight inner loops, that'd need language and optimizer support.
Some thoughts on implementing:
  • for_if means literally `if`. Thus there are `else` / `else if` branches. Note that the loop might reenter multiple times, therefore the else branches are not mutually exclusive, nor can you expect that execution directly continues from `else` to next instructions. The `else` / `else if` branches are part of the loop. The keywords `continue` and `break` work as expected in the body of the loop.
  • It is possible to continue the same loop after break. You can either reenter (same loop var) or continue_from_break (next loop var). If you continue (either way), you'll get the `else` branch at the end of the loop. Particularly, if you break at the last element, then continue_from_break, you'll get to the `else` branch again. If this is not desirable, test for `!_broken` instead of `!break` for done event. Thus you get the following seven events: (with variables available in `else` only)
    • entering the body of the loop
    • range was empty: else if (!_empty)
    • first break: else if (_break && !_broken), _begin .. _end is the remaining range including the current element
    • subsequent break: else if (_break && !_broken), _begin .. _end is the remaining range including the current element
    • loop finished without break: else if (!_break && !_broken)
    • loop finished after reenter / continue_from_break: else if (!_break && _broken)
  • It makes more sense to me to have a view on the entire range in break handler, based on iterators. That allows manipulating the range before exit / reentry. Let me know if you have strong preference for loop variable there.
  • Another interesting feature that's not implemented here is special handling of the first element (prelude). Ideally that'd be unrolled by the compiler, an `if (_first)` is too bad performance-wise (and manual loop body duplication is a no-go).
  • After considering multiple solutions, I decided to say `break` and `continue` are UB from the `else` branch. I feel particularly bad about `for (v : vs) for_if (i : v) break; else break;`, where the two `break` statements would return to different places. That's simply unnatural and has a great potential for ending up in errors. Instead, we have now label-based `break_to()` and `continue_to()`, mimicking the proposal on `break label;` and `continue label;`.
  • In for_if, only auto, auto&, auto&& declarations and predeclared variables are supported. So far I see this rather an advantage than limitation, but let me know if you feel differently.
  • It can be used as a single statement, but note that `else` binds to it.
  • It's not coroutine-compatible, sorry. (You can't goto or `switch`-jump into for_if or its `else`.)
  • Several things could make it easier; incl. `constexpr?:` expressions and statements, `if()` interpreting declarations that can't cast to bool / integer as true, macros allowing `:` (and perhaps `;` - or anything else) as separator; perhaps even c++-like parentheses. On the other thread I brought up the idea of `for_impl (auto&& i : v) { ... }` meaning like `for_impl( [&, take_return, take_break, take_continue](auto&& i) { ... })(v);`, i.e., to make `for` an operator that transforms an element-wise lambda to one that takes a range. That's one way to go and, when done, it'd simplify optional visitation, make index-based loops trivial; but that needs heavy language support.
If anyone dares to use it, or experiment with it, I'd be more than glad for any feedback, which is essential for the further steps in standardization of any of these.

Thanks,
-lorro

for.cpp
Reply all
Reply to author
Forward
0 new messages