3

I've got a problem using a default value for a function parameter.

This code gives "'blink' was not declared in this scope":

void loop(void) {
  blink(12, 2, 1000);
}

void blink(const uint8_t led, int num, const uint16_t l = 12) {
  Serial.println(led);
  Serial.println(num);
  Serial.println(l);
}

but it works if I don't assign a default value to the "l" arg:

void blink(const uint8_t led, int num, const uint16_t l) {

or if I put the blink function before the loop:

void blink(const uint8_t led, int num, const uint16_t l = 12) {
  Serial.println(led);
  Serial.println(num);
  Serial.println(l);
}

void loop(void) {
  blink(12, 2, 1000);
} 
2
  • 5
    only a function declaration can have a default parameter value. not the definition. so you have to add a forward declaration if you want to set default parameter values.
    – Juraj
    Commented Jan 25, 2021 at 18:09
  • 2
    Confusing the code mangler that sits between your IDE code and the GCC code it generates is a sure way to get oddball errors. Commented Jan 26, 2021 at 20:26

2 Answers 2

11

It's just a failing of the Arduino build process to generate prototypes (function declarations) for functions that have default argument values. There's otherwise nothing wrong with what you're doing. Not in my opinion anyway. The build process sometimes chokes on function or class templates as well. The answer to these sorts of problems is probably that these features are not considered part of the Arduino "language".

Just a reminder: In C++ a function declaration needs to have been seen in order to generate a call to it. A definition will serve as a declaration. In either case, one must have been seen before the call site. Generating declarations where needed is, perhaps, the greater part of what the Arduino build process does that can be said to make the Arduino "language" different from plain C++.

The following were generated by working out of /tmp/default-param with the "input" being default-param.ino, running:

arduino-cli -b arduino:avr:uno build --build-path /tmp/build

and inspecting /tmp/build/sketch/default-param.ino.cpp which I'm calling "output". You can use the same process if you want to see exactly what the build process is doing to your code.

Non-default argument

This input:

void setup() {
    func(11);
}
void loop() {}
void func(int xyzzy) {}

generates:

#include <Arduino.h>
#line 1 "/tmp/default-param/default-param.ino"
#line 1 "/tmp/default-param/default-param.ino"
void setup();
#line 4 "/tmp/default-param/default-param.ino"
void loop();
#line 5 "/tmp/default-param/default-param.ino"
void func(int xyzzy);
#line 1 "/tmp/default-param/default-param.ino"
void setup() {
    func(11);
}
void loop() {}
void func(int xyzzy) {}

The build process has inserted a valid C++ function declaration prior to the call site, so the compiler is happy.

With default argument

But this input:

void setup() {
    func(11);
}
void loop() {}
void func(int xyzzy = 7) {}

generates:

#include <Arduino.h>
#line 1 "/tmp/default-param/default-param.ino"
#line 1 "/tmp/default-param/default-param.ino"
void setup();
#line 4 "/tmp/default-param/default-param.ino"
void loop();
#line 1 "/tmp/default-param/default-param.ino"
void setup() {
    func(11);
}
void loop() {}
void func(int xyzzy = 7) {}

No declaration prior to the call site this time, so the compiler is not happy with this generated code.

Build process

The build process is a rather ugly... process... that scrapes the output of the compiler and generated ctags to build include paths and to try to generate forward references for things. Sometimes, it just gets it wrong. I'm not sure what the exact error is in the build process, but it is probably just a failure to match the error caused by calling an undeclared function with its matching entry in the ctags. You can try reading through the build process documentation to make sense of this. Ultimately though, you may have to read the source to arduino-cli/arduino-builder to get to exact cause.

Non-.ino (and non-.pde) files more or less go through the build process unscathed. So, if you really want default arguments, you can make use of and .h file for your function prototype/declaration and a .cpp file for its body and follow the normal rules of C++.

Using your own declaration

You can just make your own declaration for it. Doing so makes it not detect an error on that line.

void func(int xyzzy = 7);

void setup() {
    func(11);
}
void loop() {}
void func(int xyzzy) {}

Output:

#include <Arduino.h>
#line 1 "/tmp/default-param/default-param.ino"
void func(int xyzzy = 7);

#line 3 "/tmp/default-param/default-param.ino"
void setup();
#line 6 "/tmp/default-param/default-param.ino"
void loop();
#line 7 "/tmp/default-param/default-param.ino"
void func(int xyzzy);
#line 3 "/tmp/default-param/default-param.ino"
void setup() {
    func(11);
}
void loop() {}
void func(int xyzzy) {}

As you can see it generates a proto type for whatever reason. But not one that conflicts with your own.


It is probably better if you want to make use of default arguments to just use headers and .cpp file for those functions that have them so that you're not subject to changes in the ability of arudino-cli/arduino-builder to handle them.

2
  • 3
    Solution is buried in answer: "You can just make your own declaration for it". Suggestion: move to top, or add a TLDR at top.
    – Mathieu K.
    Commented Jul 30, 2022 at 18:04
  • Probably when I wrote this I took the "or if I put the blink function before the loop" to mean that they already had a solution and were wondering about why, so the TLDR is more of a why-TLDR "It's just a failing of the Arduino build process to generate prototypes" rather than a how-TLDR. They weren't explicit in asking so both are covered. If you're saying you found this helpful but didn't like reading as far, sure, I'll see if I can't address the how part sooner also.
    – timemage
    Commented Jul 30, 2022 at 18:28
0

If you use a function after you call it in the same file, you have to (or at least it is better) to add a so called 'forward declaration' before calling it. The best place is before the first function, and in your case it should look like:

void blink(const uint8_t led, int num, const uint16_t l);

You can omit the = 12 or not.

2
  • 2
    Normaly this is done by the Arduino IDE, and when I don't put a default value "blink" can be define after the "loop" function, without a 'forward declaration' before calling it. I don't understand why this problem error exist only with default parameter.
    – MichelBen
    Commented Jan 25, 2021 at 17:47
  • The reason behind it probably can better be asked in StackOverflow. Commented Jan 25, 2021 at 20:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.