How to use enum without include files?

I can’t for the life of me figure this out… I can’t get this to compile.

I based my code on https://github.com/synox/Spark-Busstop-Light/blob/master/spark_core/application.cpp where @Coffee uses an enum. Even though he includes application.h at the top that I don’t have, when I paste the code into the Web IDE, it compiles just fine.

However, when I munged the code for my purposes, I get

../57de2889598ee51f77455b1d66f1ecf0703bb94ca54721b3998679a9f9ab/the_user_app.cpp:3:16: error: variable or field 'updateLED' declared void
../57de2889598ee51f77455b1d66f1ecf0703bb94ca54721b3998679a9f9ab/the_user_app.cpp:3:16: error: 'Status' was not declared in this scope
../57de2889598ee51f77455b1d66f1ecf0703bb94ca54721b3998679a9f9ab/the_user_app.cpp:4:1: error: 'Status' does not name a type
make: *** [../57de2889598ee51f77455b1d66f1ecf0703bb94ca54721b3998679a9f9ab/the_user_app.o] Error 1

I’m guessing this is related to the FAQ in http://playground.arduino.cc/Code/Enum. But why does it work for the code I based it on?

    //#include "umbrella3.h"

//Originally based on https://github.com/synox/Spark-Busstop-Light/blob/master/spark_core/application.cpp


// ------------- configuration ------------

#define DEBUG true

// possible status list:
enum Status {
        clear=0, rain=1, snow=2, thunder=3
};

const char* host = "myhiddenhost.no-ip.com";  //redacted
int port = 80;
const char* query = "/rain";
int led = D7;

long lastTsMilis  = 0;
unsigned int nextTime = 0;    // next time to contact the server



// Support functions

void updateLED(Status stat) {
        switch(stat) {
                case clear:     RGB.color(0,0,10); break;       // dimmed blue
                case rain:      RGB.color(255,0,0); break;      // red
                case snow:      RGB.color(255,50,0); break;     // orange
                case thunder:   RGB.color(255,150,0); break;    // yellow
        }
        return;
}


Status parseIt(String jsonData) {
        int offset = 0;
        do {
                offset = jsonData.indexOf("rain:", offset);
                if(DEBUG) Serial.print("offset: ");
                if(DEBUG) Serial.println(offset);

                if (offset == -1) {
                        break;
                }
                offset += 6; // move to result value
                String str = jsonData.substring(offset, offset + 1);
                if(DEBUG) Serial.print("result: ");
                if(DEBUG) Serial.println(str);
                if(str.length() == 0) {
                        continue;
                }
                //return (Status) str.toInt();
                switch (str.toInt()) {
                    case 0: return clear; break;
                    case 1: return rain;  break;
                    
                    default: return thunder; break;
                }
        } while (offset >= 0);

        return thunder;
}




// ------------- HTTP functions --------------


//make http request and return body
TCPClient client;
char buffer[512];
String http_get(char const* hostname, int port, String path) {

        if (client.connect(hostname, port)) {
                client.print("GET ");
                client.print(path);
                client.print(" HTTP/1.0\n");
                client.print("HOST: ");
                client.println(hostname);
                client.print("\n");
                client.flush();
        } else {
                Serial.println("connection failed");
                client.stop();
                return NULL;
        }

        // Block until first byte is read.
        client.read();
        for (unsigned int i = 0; i < sizeof(buffer) && client.available(); i++) {
                char c = client.read();
                if (c == -1) {
                        break;
                }
                buffer[i] = c;
        }
        client.stop();

        String response(buffer);
        int bodyPos = response.indexOf("\r\n\r\n");
        if (bodyPos == -1) {
                Serial.println("can not find http reponse body");
                return NULL;
        }
        return response.substring(bodyPos + 4);
}



// Setup
void setup() {
        Serial.begin(9600);
        pinMode(led, OUTPUT);

        RGB.control(true);
        RGB.color(0,0,10);   // dimmed blue
}


//Looooooooooop forever
void loop() {
    
    void updateLED(Status);
    enum Status {
        clear=0, rain=1, snow=2, thunder=3
    };

    if (nextTime > millis()) {
            // keep the same color while waiting
            return;
    }

    digitalWrite(led, HIGH);
    delay(200);
    digitalWrite(led, LOW);


    // the timestamp is checked online every 5 min
    if(lastTsMilis == 0 || millis() - lastTsMilis > 5 * 60 * 1000 ) {
            Serial.println("local time is old, refreshing");
            // refresh time online
            lastTsMilis = millis();
    }


    String resp = http_get(host, port, query);

    updateLED(parseIt(resp));

    // check the color again in 5 seconds:
    nextTime = millis() + 5000;
}

I haven’t cleaned this up any or even really thought about the logic, I’m just trying to get this to compile in the Web IDE so I can get to the next step.

Is there a reason you declared the enum Status twice, once with global scope near the top and again in void loop()?

It should compile without the second one; maybe that’s an area to look at?

I don’t know if this behavior is inherited from the arduino ide or what, but the ide is ungodly stupid and horrible. It rearranges and scrambles your code. Not only does it not tell you what it does, but it doesn’t do it correctly. Note that it doesn’t just move code around, but it also tries to insert function prototypes into functions like loop() (and, did I say that it doesn’t do it correctly?).

In your case, it appears that it can’t handle enums, and screws it up when it comes to inserting the prototype for parseIt() into loop(). Note that the compiler isn’t telling you that parseIt() is undefined (as it probably should be), but is instead complaining about Status because the ide appears to have screwed up the prototype for parseIt(). To workaround this stupidity, two things need to be done:

  1. You need to manually add the correct prototype for parseIt() to loop.

  2. You need to trick the stupid ide into properly handling the enum. There may be other ways, but the only way, that I know about, is to wrap the enum into a class.

This seems to compile:

//#include "umbrella3.h"

//Originally based on https://github.com/synox/Spark-Busstop-Light/blob/master/spark_core/application.cpp


// ------------- configuration ------------

#define DEBUG true

// possible status list:
class StupidClass
{
    public:
    enum Status {
        clear=0, rain=1, snow=2, thunder=3
    };
};

const char* host = "myhiddenhost.no-ip.com";  //redacted
int port = 80;
const char* query = "/rain";
int led = D7;

long lastTsMilis  = 0;
unsigned int nextTime = 0;    // next time to contact the server



// Support functions

void updateLED(StupidClass::Status stat) {
        switch(stat) {
                case StupidClass::clear:     RGB.color(0,0,10); break;       // dimmed blue
                case StupidClass::rain:      RGB.color(255,0,0); break;      // red
                case StupidClass::snow:      RGB.color(255,50,0); break;     // orange
                case StupidClass::thunder:   RGB.color(255,150,0); break;    // yellow
        }
        return;
}


StupidClass::Status parseIt(String jsonData) {
        int offset = 0;
        do {
                offset = jsonData.indexOf("rain:", offset);
                if(DEBUG) Serial.print("offset: ");
                if(DEBUG) Serial.println(offset);

                if (offset == -1) {
                        break;
                }
                offset += 6; // move to result value
                String str = jsonData.substring(offset, offset + 1);
                if(DEBUG) Serial.print("result: ");
                if(DEBUG) Serial.println(str);
                if(str.length() == 0) {
                        continue;
                }
                //return (Status) str.toInt();
                switch (str.toInt()) {
                    case 0: return StupidClass::clear; break;
                    case 1: return StupidClass::rain;  break;

                    default: return StupidClass::thunder; break;
                }
        } while (offset >= 0);

        return StupidClass::thunder;
}




// ------------- HTTP functions --------------


//make http request and return body
TCPClient client;
char buffer[512];
String http_get(char const* hostname, int port, String path) {

        if (client.connect(hostname, port)) {
                client.print("GET ");
                client.print(path);
                client.print(" HTTP/1.0\n");
                client.print("HOST: ");
                client.println(hostname);
                client.print("\n");
                client.flush();
        } else {
                Serial.println("connection failed");
                client.stop();
                return NULL;
        }

        // Block until first byte is read.
        client.read();
        for (unsigned int i = 0; i < sizeof(buffer) && client.available(); i++) {
                char c = client.read();
                if (c == -1) {
                        break;
                }
                buffer[i] = c;
        }
        client.stop();

        String response(buffer);
        int bodyPos = response.indexOf("\r\n\r\n");
        if (bodyPos == -1) {
                Serial.println("can not find http reponse body");
                return NULL;
        }
        return response.substring(bodyPos + 4);
}



// Setup
void setup() {
        Serial.begin(9600);
        pinMode(led, OUTPUT);

        RGB.control(true);
        RGB.color(0,0,10);   // dimmed blue
}


//Looooooooooop forever
void loop() {

    void updateLED(StupidClass::Status);
    StupidClass::Status parseIt(String jsonData);

    if (nextTime > millis()) {
            // keep the same color while waiting
            return;
    }

    digitalWrite(led, HIGH);
    delay(200);
    digitalWrite(led, LOW);


    // the timestamp is checked online every 5 min
    if(lastTsMilis == 0 || millis() - lastTsMilis > 5 * 60 * 1000 ) {
            Serial.println("local time is old, refreshing");
            // refresh time online
            lastTsMilis = millis();
    }


    String resp = http_get(host, port, query);

    updateLED(parseIt(resp));

    // check the color again in 5 seconds:
    nextTime = millis() + 5000;
}

@bko, ignore that, it was just one of a million attempts to get it to compile.

@Raldus, wow. That’s a lot of hoops to jump through for something as simple as an enum. I’ll give it a shot.

@zach, is this on the buglist already?

Yes, we’ve got a whole suite of fixes to the pre-processor in the works right now. The pre-processor adds #includes and function prototypes, and is based on Arduino’s (which does the same thing), but there are some parsing issues that we’re working out now.

As a future enhancement, please give us an option/button to dump out all of the mangled source files into a single web page. That way, we can actually figure out what the errors are. Extra brownie points for:

  • Line numbers
  • Attaching corresponding errors to each source file.
  • Showing -Wall warnings.
1 Like

Feedback noted, thanks @Raldus

1 Like

Oh Sparkulator, I still :blue_heart: you... pay no attention to that bully @Raldus :stuck_out_tongue: We'll enroll you in night classes, and then you can anticipate Raldus' code syntax errors and warn him before he even starts to type.

1 Like

I am joining this discussion, because now I also have the “was not declared in this scope” Problem on the Web IDE (with the code refered to in the first post). I didn’t notice this error because I compiled all my code locally and flashed over dfu, and there was no problem.

@zach Should this be fixed with sprint 4, or is this still in backlog?

@Coffee We just made a ton of fixes to the IDE and I don’t know of any outstanding issues that we’re currently aware of. Can you share the code that causes the compile error so that we can test it and add a fix?

@zach This should compile, but errors:

typedef enum Status {
    clear=0, rain=1, snow=2, thunder=3
};

Status parseIt(String jsonData) {
    return thunder;
}

void setup() {

}

void loop() {
    parseIt("asdfasdfasdfasdf");
}
the_user_app.cpp:3:1: error: 'Status' does not name a type
the_user_app.cpp:3:1: warning: 'typedef' was ignored in this declaration [enabled by default]
make: *** [the_user_app.o] Error 1

Cool, thanks @BDub!

:spark:

Adding this to my list of compiler tasks, another round of fixed coming today. Thanks, and please keep the bugs rolling in!

Here is another example, but very similar to @BDub

1 Like

@Coffee, that’s the same bug as the original poster’s, and it has the same workaround.

Hey Guys,

Thanks again for keeping this thread going. Essentially what’s happening is that the web IDE is trying to do you a favor by adding back in missing function declarations. Without these your file wouldn’t compile at all.

Adding these back in obviously becomes a problem when there are other new types that need to be evaluated in a particular sequence. Right now the best workaround when adding your own types is to correctly add your own function declarations as you would normally when writing C/C++. Injecting these missing pieces automatically in the right place is a bit tricky, and I’m still testing my solution, but I wanted you to know I’m still working on it. My plan is to have a fix for this ready and rolled out tomorrow morning. This fix will still require at least the first original function definition to be after any new types. Please let me know if you don’t think that’s a good trade-off.

I like the idea of outputting the pre-processed source, that’s certainly something we could look into making available.

Thanks,
David

example workaround for earlier code:

enum Status {
	off=1, missed=2, run=3, leave_now=4, walk=5
};

Status getStatus(int diffSeconds);  //add me here
Status getStatus(int diffSeconds) {
	if (diffSeconds < 60) {
		return off;
	} else {
		return walk;
	}
}


void setup() {
    Status s = getStatus(70);
}

void loop() {
}

How about an expert option that says, “do not rearrange the code”? Just #include the user’s code, as-is, in one big chunk, in whatever framework you’re using. I think that, by trying to make this user-friendly, it’s just causing headaches for regular developers; we know how to do function prototypes/etc. and how to order our code.

Unfortunately that would breaks with our promise of “Arduino-compatible” - we are doing what the Arduino pre-processor does to the .ino file, and that makes it a lot easier for novices to work with. I realize it’s a bit buggy right now but once we work through the bugs, this shouldn’t be an issue anymore.

In fact I’ll point out that @BDub’s example above doesn’t compile in the Arduino IDE for the same reason.