1

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.

  • Arduino builder has problems if structs, enums, classes are not defined before the first function – Juraj Jun 23 at 12:44
  • why do you write enum class instead of just enum? – chrisl Jun 23 at 14:47
  • 1
    @chrisl, enum class or enum struct from C++11 is enum where the values can be used only with the enum type name like CYCLE::TypeB. – Juraj Jun 23 at 15:46
  • 1
    Could you please provide an example that reproduces this comiler error? I embedded your code in the setup()-loop() template and it works! – Sim Son Jun 23 at 16:41
  • Done @SimSon and that was where the "bug" was lurking. See Juraj's answer. – AJP Jun 23 at 22:28
3

This is a 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 Jun 23 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. – Juraj Jun 24 at 7:02
  • Done. Thanks @Juraj – AJP Jun 24 at 9:49
1

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:

  1. (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.
  2. Put each type/enum value on a new line; this makes it easier to add comments above/after it.
  3. 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.
  4. Use a switch statement instead of multiple if 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 of switch: does C++ error / warn if you don't handle all enum types? Only return once pattern: yes completely agree. Thank you. – AJP Jun 23 at 22:05
  • 1
    You'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 Jun 23 at 22:12
  • Right. I understand. In your experience is the if (a = x) more prevalent as a bug than forgetting the break? 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 eschew switch for that reason. – AJP Jun 23 at 22:27
  • 1
    You 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 Jun 23 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 Jun 23 at 22:40
0

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
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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