I'm not sure if someone's already mentioned this, but is there anything wrong with making this into a "while"?
To increment, or not to increment? That is the question.
Every text processing loop I've ever written has been a "while" loop for this reason.
Sometimes you just need to check the next character without consuming it, and sometimes you need to take a step larger than one character.

Try something like making a LineInput class with a PushBack( c ) that returns "true" if it detected an EOL condition.

Then your loop might be:

while( Serial.available() )
{
  if( line.PushBack( Serial.read() ) )
  {
    // We have a complete line, parse it.
    if( ! ProcessCommand( line.GetPtr() ) ){ break; }// Break if the command met a terminal condition
  }
}

I'm aware of the limitations of embedded environments like an Arduino, and so understandably there should really be more error handling to do with the buffer filling up and other things.
However, this is just one challenge in dealing with unconstrained serial communications; QoS issues being the much more difficult set of problems.
It pays to have layered code that can separate QoS issues from actual behavioral implementation, and something like a LineInput can help with that.

I've often found myself in need of some way to break out of nested loops, or break from a loop within a switch statement, but then later found it could be refactored into something more readable.

On Sun, Nov 5, 2023 at 1:39 PM Frederick Virchanza Gotham via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
Today I was programming an Arduino microcontroller and I wrote the
following loop:

            for ( ; Serial.available(); ++p )
            {
                if ( p >= &buf[sizeof(buf) - 1u] ) break;
                p[0u] = Serial.read();
                p[1u] = '\0';
                unsigned const len = std::strlen(buf);
                char *ending = nullptr;
                if ( nullptr != (ending = std::strstr(buf, "\r\n")) )
                {
                    ending[0u] = '\0';
                    ending[1u] = '\0';  // Overwrite the "\r\n" with "\0\0"
                    if ( ending != buf ) this->ProcessInput();
                    buf[0u] = '\0';
                    p = buf - 1u;  // subtract 1 because it will be
incremented upon continuing
                }
            }

On the third last line, you'll see that I wrote:

    p = buf - 1u;  // subtract 1 because it will be incremented upon continuing

instead of just:

    p = buf;

and that's because 'p' will get incremented upon the next iteration of
the loop, i.e. the 'for' loop has '++p' as the post-iterative step.

This code I've written today will work fine on a microcontroller, but
on a desktop PC that has extreme debugging enabled (for example
'mudflap' or 'address santiser'), the debugger will stop the program
when it sees that 'p' has become less than 'buf'.

So I was thinking . . . what if we could easily express that we want
to continue on to the next iteration of the loop, but that we don't
want to execute the post-iterative step. Maybe we could write
"!continue" for this, something like:

            for ( ; SerialSimGsm.available(); ++p )
            {
                if ( p >= &buf[sizeof(buf) - 1u] ) break;
                p[0u] = SerialSimGsm.read();
                p[1u] = '\0';
                unsigned const len = std::strlen(buf);
                char *ending = nullptr;
                if ( nullptr != (ending = std::strstr(buf, "\r\n")) )
                {
                    ending[0u] = '\0';
                    ending[1u] = '\0';  // Overwrite the "\r\n" with "\0\0"
                    if ( ending != buf ) this->ProcessInput();
                    buf[0u] = '\0';
                    p = buf;
                    !continue;  // Don't allow 'p' to be incremented
                }
            }

In this example, the post-iterative step is quite simple, but there
are other times when I write loops like:

            for ( ; Serial.available(); delayMicroseconds(750u), ++p )
or:

            for ( ; Serial.available(); Serial2.write(Serial.read()) )
DoSomethingInBetween();

and so the statement "!continue" could be quite versatile in these
more complex loops.
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals