Explanation of stack overflow during compilation

I have been working on a program for a while and it was compiling/executing well. Then I added a new function to it, to display progress in the console, and it will no longer compile. The following error appears and I have to close the IDE to continue.

Exception in thread “Thread-255” java.lang.StackOverflowError
at java.lang.Character.codePointAt(Unknown Source)
"followed by many (repeating?) lines of java references"

The function that was added was developed in a new sketch, and copied into the program. The short sketch demonstrating the function compiles follows:

int optPArg=-1;

void displayStep(String myAction,int& myDesc=optPArg, int& myOption=optPArg, int& myStack=optPArg)
{
  Serial.print(myAction+" ");
  if (myDesc>=0) {
     Serial.print(myDesc);
     Serial.print(" ");
  }
  if (myOption>=0) {
     Serial.print(myOption);
     Serial.print(" ");
  }
  if (myStack>=0) {
     Serial.print(myStack);
     Serial.print(" ");
  }
}
  
  

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

The program which no longer compiles is attached.

What causes stack overflows during compilation and what do I need to do to get the program to comple?

Animatron_8_3.ino (12.2 KB)

pMoveCommands.h (4.81 KB)

CompileError.txt (60.4 KB)

djsfantasi: void displayStep(String myAction,...

Your program is using 'String' objects instead of using 'strings' (char arrays, C-strings, zero terminated strings).

That's a big mistake for any non trivial microcontroller program with limited RAM.

Probably nothing to do with the added function.

          case PlayMove: { //1
            DisplayStep("PlayMove,ScriptDescription[ScriptStep]);

You have an unterminated quoted string. That will blow up the IDE preprocessor.

Pete

Optional arguments are declared in the function prototype, NOT in the function definition.

PaulS: Optional arguments are declared in the function prototype, NOT in the function definition.

It was the mismatched quote. I forgot that I added that line and found it with a comparison of different revisions of the source.

PaulS, I didn't quite get what you mean.

PaulS, I didn’t quite get what you mean.

It appears that you want to be able to call displayStep() with just a String, and have the other arguments default to specific values. That is NOT the correct way to do it.

You need to define a function prototype where the arguments are assigned default values. In the function declaration, you do NOT define default values.

PaulS: It appears that you want to be able to call displayStep() with just a String, and have the other arguments default to specific values. That is NOT the correct way to do it.

You need to define a function prototype where the arguments are assigned default values. In the function declaration, you do NOT define default values.

I used an example from the Forum. I understand that Arduino does not refer to explicitly defined function prototypes; Arduino creates function prototypes automatically for you. (see your reply in this thread)

I want to call displaystep with a variable number of arguments. The default values are of no consequence to me, but needed as far as I can tell to define optional arguments. Please clarify if I am wrong.

As I have coded it, it work as far as I can tell. So while I am coming from a place of "if it ain't broke, don't fix it", I'd much rather learn better practice.

In any case, default arguments, passed by reference, is kind-of weird.

Why pass by reference if the function doesn't change them?

[quote author=Nick Gammon link=msg=2082610 date=1423436735] In any case, default arguments, passed by reference, is kind-of weird.

Why pass by reference if the function doesn't change them? [/quote]

Good question! Do you know of another way to pass a variable number of arguments? As I understood it, using default values or overloading the function definition by writing n number of versions of the function are the only ways to do it. The latter case is extremely wasteful of code space when the former method works just fine. Seriously, if you hve another way, please let me know.

Well, using your general technique, but not using passing by reference:

void displayStep(const char * myAction, int myDesc = -1, int myOption = -1, int myStack = -1)
{
  // whatever
}
void setup () {} 

void loop () 
{
  displayStep ("foo");
  displayStep ("foo", 1);
  displayStep ("foo", 1, 2);
  displayStep ("foo", 1, 2, 3);
}

You could use stdarg, but that is a bit fiddly. If you have a maximum of 3, this technique should be OK.

Another approach would be to pass an int array, along with a counter, so you know how many elements in the array to access.

[quote author=Nick Gammon link=msg=2082640 date=1423438111] Well, using your general technique, but not using passing by reference: [/quote]

Well, I'll be...

Thanks.

I've thought about it and I ended up passing by reference in this case, because I was modelling it after another function, which DID require passing by reference, as I required passing back multiple values from the function.

Thanks for providing me with the example. I'm just learning this stuff and have a 500 line sketch working.

having multiple, probably unused, optional arguments is also "wasteful of code space". If I wanted to do that, I'd use an array of pointers.

michinyon: having multiple, probably unused, optional arguments is also "wasteful of code space". If I wanted to do that, I'd use an array of pointers.

[quote author=Nick Gammon link=msg=2082652 date=1423438422] Another approach would be to pass an int array, along with a counter, so you know how many elements in the array to access. [/quote]

I thought about that, but didn't think it was appropriate. Please let me explain why.

The function is called ~18 times throughout the program. Each time with different variables, I thought I'd have to preceed each call with at least ~5 lines to set up the array of pointers and a count.

Secondly, all "optional arguments" are used at some location of the sketch. There are places which use 1, 2 or 3 of the optional arguments.

Thirdly, the function is a progress display. I thought of other ways as well, using Global variables, but it obfuscated what the function was doing in each call. This way, it is self-documenting as well as indicating which variables are used in different cases. Personal call.