orglce
orglce

Reputation: 543

Break from a loop with an external method

I am programming with an Arduino and my program contains a lot of while loops. When Arduino receives a character it must do some calculations and break from a loop that it has been in when it received a character. I will give you a simpler example (presume i and j are set to 0):

while (i < 256)
{
    // some calculations #1
    i++;

    if (Serial.available() > 0)
    {
        setStringOne = "string one"
        setStringTwo = "string two"
        setStringThree = "string three"
        setStringFour = "string four"

        break;
    }
}

while (j < 256)
{
    // some calculations #2
    j++;

    if (Serial.available() > 0)
    {
        setStringOne = "string one"
        setStringTwo = "string two"
        setStringThree = "string three"
        setStringFour = "string four"

        break;
    }
}

You can see that in both cases I used the same piece of code in the if sentences. What I want it to be able to write something like this.

while (i < 256)
{
    // some calculations #1
    i++;

    if (Serial.available() > 0)
        checkAndBreak();
}

while (j < 256)
{
    // some calculations #2
    j++;

    if (Serial.available() > 0)
        checkAndBreak();
}

void checkAndBreak()
{
    if (Serial.available() > 0)
    {
        setStringOne = "string one"
        setStringTwo = "string two"
        setStringThree = "string three"
        setStringFour = "string four"

        break;
    }
}

To break for a loop with an external method.

It gives me an error "break statement not within loop or switch", which is expected since it doesn't know which loop to break from but I was just wondering if it is possible to make something along those lines.

Thanks in advance!

Upvotes: 2

Views: 406

Answers (3)

oopexpert
oopexpert

Reputation: 755

This is a perfect example why you should not use "break", "continue" or multiple return statements within a method/procedure. You are not able to extract methods to reduce redundancies. My advice is to reformulate your code to work without break-statements.

I would try following approach:

do {
    // some calculations #1
    i++;
} while (i < 256 && Serial.available() == 0)

if (Serial.available() > 0)
{
    setStringOne = "string one"
    setStringTwo = "string two"
    setStringThree = "string three"
    setStringFour = "string four"
}

do {
    // some calculations #2
    j++;

} while (j < 256 && Serial.available() == 0)

if (Serial.available() > 0)
{
    setStringOne = "string one"
    setStringTwo = "string two"
    setStringThree = "string three"
    setStringFour = "string four"
}

Now you are able to improve the the code:

Extract methods and apply the template pattern to the calculation code (as far as possible in Arduino) to reduce redundancy.

Example of extracted method:

void determineString()
{
    if (Serial.available() > 0)
    {
        setStringOne = "string one"
        setStringTwo = "string two"
        setStringThree = "string three"
        setStringFour = "string four"
    }
}

Upvotes: 1

kikobyte
kikobyte

Reputation: 344

You can do similar tricks with return, though it might look little less obvious for some old-school programmers. Assuming Serial.avaiable() returns unsigned integer, you don't want to check it before your calculations, and you do actually need the updated i,j counters as the post-effects when you update your strings, I would do it this way:

// Obviously this function somehow has
// access to the strings being modified
// And possibly to some other state affected by calculations #1 and #2
// I'm leaving it similar to how you've written it
// for the sake of clarity
bool checkAndBreak()
{
    if (!Serial.available())
        return false;

    setStringOne   = "string one";
    setStringTwo   = "string two";
    setStringThree = "string three";
    setStringFour  = "string four";

    return true;
}

size_t i = 0, j = 0;

do {
    // calculations #1
} while (++i, !checkAndBreak() && i < 256);

do {
    // calculations #2
} while (++j, !checkAndBreak() && j < 256);

Note that in the end i,j yield the same values as in your example. Another funny way is:

// define `i,j` outside of the loops if you need their final values
// !i and !j checks are to make sure checkAndBreak() 
// won't execute before the loop body
for (size_t i = 0; (!i || !checkAndBreak()) && i < 256; ++i)
    // calculations #1
for (size_t j = 0; (!j || !checkAndBreak()) && j < 256; ++j)
    // calculations #2

You can also extract your calculations into functions:

void calc1(size_t i) { ... }
void calc2(size_t j) { ... }

for (size_t i = 0; i < 256 && (calc1(i++), !checkAndBreak()); );
for (size_t j = 0; j < 256 && (calc2(j++), !checkAndBreak()); );

This way you get your < 256 check first, then calc1/calc2 are executed with the old i/j values, afterwards the i/j value gets incremented, and then the check is performed. If the check returns true, the whole for loop terminates.

Beware that those practices are not aligned with the KISS principle, so may you adjust your code, pre-conditions and post-effects to simply write

for (size_t i = 0; i < 256; ++i)
{
    // do the calculations #1
    // now check the side effects of those calculations
    if (serialCheckedAndStateChanged())
        break;
}
// The same for #2

Upvotes: 1

CakePlusPlus
CakePlusPlus

Reputation: 993

You can't break like that, so no way. Just balance out what each method does:

while (i < 256)
{
      if (Serial.available() > 0)
      {
             setThoseStrings();
             break;
      }
      i++;
}

Alternatively

while (i < 256)
{
        if (checkSerialAndSetStrings())
        {
               break;
        }
        i++;
}

This looks shorter but if you ever need to set strings in other situations (e.g. setting them when a timer runs out), you'll just waste time removing the serial check from checkSerialAndSetStrings and updating your code. I'd go with #1.

Upvotes: 6

Related Questions