I am programming an Arduino. In the same .ino file as setup()
and loop()
I have defined the following:
void setup()
{
// setup code
}
enum class CYCLE { TypeA, TypeB };
String cycleToString (CYCLE cycle) {
if (cycle == CYCLE::TypeA) {
return "TypeA";
}
else if (cycle == CYCLE::TypeB) {
return "TypeB";
}
return "Undefined";
}
void loop()
{
// loop code
}
But on compiling it gives the error:
sketch_v1:37:22: error: 'CYCLE' was not declared in this scope
String cycleToString (CYCLE cycle) {
^
/Users/.../sketch_v1.ino: In function 'String cycleToString(CYCLE)':
sketch_v1:37:31: error: 'String cycleToString(CYCLE)' redeclared as different kind of symbol
String cycleToString (CYCLE cycle) {
^
/Users/.../sketch_v1.ino:37:8: note: previous declaration 'String cycleToString'
String cycleToString (CYCLE cycle) {
^
Note that the enum class
is fine and works as intended but if I try to add the function relying on the CYCLE
type it then errors.
4 Answers 4
This is a known bug of Arduino builder, if you have functions before structs, classes or enums in sketch.
The sketch:
void foo() {
}
enum class CYCLE { TypeA, TypeB };
String cycleToString (CYCLE cycle) {
if (cycle == CYCLE::TypeA) {
return "TypeA";
}
else if (cycle == CYCLE::TypeB) {
return "TypeB";
}
return "Undefined";
}
void setup() {
}
void loop() {
}
is processed to this .cpp file
#include <Arduino.h>
#line 1 "/tmp/arduino_modified_sketch_327776/sketch_jun23a.ino"
#line 1 "/tmp/arduino_modified_sketch_327776/sketch_jun23a.ino"
#line 2 "/tmp/arduino_modified_sketch_327776/sketch_jun23a.ino"
void foo();
#line 7 "/tmp/arduino_modified_sketch_327776/sketch_jun23a.ino"
String cycleToString(CYCLE cycle);
#line 17 "/tmp/arduino_modified_sketch_327776/sketch_jun23a.ino"
void setup();
#line 22 "/tmp/arduino_modified_sketch_327776/sketch_jun23a.ino"
void loop();
#line 2 "/tmp/arduino_modified_sketch_327776/sketch_jun23a.ino"
void foo() {
}
enum class CYCLE { TypeA, TypeB };
String cycleToString (CYCLE cycle) {
if (cycle == CYCLE::TypeA) {
return "TypeA";
}
else if (cycle == CYCLE::TypeB) {
return "TypeB";
}
return "Undefined";
}
void setup() {
}
void loop() {
}
as you can see the forward declarations of functions are before the first function so before the enum.
The workaround for this bug is to follow good practice and declare all the structs, enums and classes before the first function definition.
-
please see my answer. If you can edit yours to include / clarify this I'll delete mine and mark yours as correct. Many thanks.AJP– AJP2019年06月23日 22:11:13 +00:00Commented Jun 23, 2019 at 22:11
-
@AJP, I know about this bug so I identified it in your Question, but I could not know what function do you have before the enum. I wrote the answer more generic. Now I added something about workaround.2019年06月24日 07:02:45 +00:00Commented Jun 24, 2019 at 7:02
-
-
2+1 for the answer and -1 for the Arduino IDE. Valid C++ should simply compile. I don't want to learn the quirks of another programming language if I can already program C++. I can't believe this hasn't been resolved in the last 4 years.Thomas Weller– Thomas Weller2023年04月26日 04:56:34 +00:00Commented Apr 26, 2023 at 4:56
-
I don't understand the workaround. I already have my enum declared before the function using it as argument, but I still get the error.AgainPsychoX– AgainPsychoX2024年01月01日 18:29:27 +00:00Commented Jan 1, 2024 at 18:29
Note this is not an answer, since your code compiles perfectly. (adding a default/empty setup/loop function).
However, I want to give some improvements that cannot fit in a comment:
- (optional) Align { and } under each other. Some put the { behind the line, some on a new line; this is mostly preference. But aligning { and } is mostly more clear.
- Put each type/enum value on a new line; this makes it easier to add comments above/after it.
- Except for trivial cases, never return from a statement, except the last one. In your case the function is quite simple, but still it's better to keep the return only at the end of a function.
- Use a
switch
statement instead of multipleif
statements.
So you get:
enum class CYCLE
{
TypeA,
TypeB
};
String cycleToString (CYCLE cycle)
{
String str;
switch (cycle)
{
case CYCLE::TypeA:
str = "TypeA";
break;
case CYCLE::TypeB:
str = "TypeB";
break;
default:
str = "Undefined";
break;
}
return str;
}
-
Thanks Michel. Completely agree with multiple lines per enum. I condensed it to aid shortening the question. I like the idea of putting the
{
and}
in vertical alignment. I've made that change now and it's good, thanks. I don't agree with the switch statement as I don't like the fall through bug if you forget break. Do common C++ IDEs warn you? As a benefit ofswitch
: does C++ error / warn if you don't handle all enum types? Only return once pattern: yes completely agree. Thank you.AJP– AJP2019年06月23日 22:05:36 +00:00Commented Jun 23, 2019 at 22:05 -
1You're welcome. An if statement can be as dangerous (using = instead of ==). I don't think you get a warning forgetting a break (you can try easily). C++ does not warn you for not checking all enum values, but that's where the 'default' keyword is for.Michel Keijzers– Michel Keijzers2019年06月23日 22:12:44 +00:00Commented Jun 23, 2019 at 22:12
-
Right. I understand. In your experience is the
if (a = x)
more prevalent as a bug than forgetting thebreak
? I guess it ultimately comes down to practice (?) but I've done the former perhaps 2 times in my life but the later a lot more so I personally eschewswitch
for that reason.AJP– AJP2019年06月23日 22:27:09 +00:00Commented Jun 23, 2019 at 22:27 -
1You might like to read: stackoverflow.com/questions/97987/… (actually I was curious too). Anyway, a switch is more readable. In all (professional) software projects I have done, a code review would not pass a bunch of if statements above a switch (when comparing against a single variable). Btw, I checked, Arduino IDE does not show any warning (not even with all warnings on).Michel Keijzers– Michel Keijzers2019年06月23日 22:39:39 +00:00Commented Jun 23, 2019 at 22:39
-
Even Visual Studio 2019 with warnings on does not show. I have worked with lint (a static C/C++ checker) and I'm fairly sure that application mentions forgotten break. Although in C it is allowed to have multiple case statements (to perform the same action for multiple values). Mostly people put // Fall through behind it, to show the break is forgotten deliberately.Michel Keijzers– Michel Keijzers2019年06月23日 22:40:15 +00:00Commented Jun 23, 2019 at 22:40
It works if you move the enum class
to the top, i.e.:
enum class CYCLE { TypeA, TypeB };
void setup()
{
// setup code
}
String cycleToString (CYCLE cycle) {
if (cycle == CYCLE::TypeA) {
return "TypeA";
}
else if (cycle == CYCLE::TypeB) {
return "TypeB";
}
return "Undefined";
}
void loop()
{
// loop code
}
I found workaround on Arduino forums: Put your enums in separate file (I use enums.hpp
), and include it from the main sketch file (.ino
) like #include "enums.hpp"
.
-
1do you have the .hpp included at the same location in the ino where you had originally the
enum
?2024年01月01日 19:00:10 +00:00Commented Jan 1, 2024 at 19:00
enum class
instead of justenum
?enum class
orenum struct
from C++11 is enum where the values can be used only with the enum type name likeCYCLE::TypeB
.setup()
-loop()
template and it works!