How the Arduino IDE tries to be too helpful

A Problem

The Arduino IDE tries too hard to be nice.

Here’s some Arduino code from a recent project of mine:

enum state_enum
{
    STOPPED,
    ACCEL,
    RUNNING,
    DECEL
};
typedef enum state_enum STATE_ENUM;
static STATE_ENUM currentState;
static void setState(STATE_ENUM newState)
{
    /* Extra code omitted for clarity */
    currentState = newState;
}
void setup()
{
    /* Extra code omitted for clarity */
    setState(STOPPED);
}
void loop()
{
    /* Extra code omitted for clarity */
    setState(RUNNING);
}

We’ve got a state variable that can be in one of four states, and a function to change state.
Looks OK, right? Well, try compiling it and you’ll get the following error:

sketch_dec04a:3: error: variable or field 'setState' declared void
sketch_dec04a:3: error: 'STATE_ENUM' was not declared in this scope

Which essentially means that on line 3 of my code, the compiler has found a datatype called “STATE_ENUM”, in the setState function, and doesn’t know what that means. But how can this be, when the function isn’t defined here, but about a dozen lines further down?

Turns out that the Arduino IDE (as part of its pre-compile process) scans your code looking for functions, generates prototypes for them and sticks them just above your actual code.

So, this line:

static void setState(STATE_ENUM newState);

Was being inserted above the declaration of the STATE_ENUM type, which causes the compiler to quite rightly say that it has no idea what STATE_ENUM is.

I can see why Arduino have done this: it means inexperienced programmers can put functions anywhere in their sketch and not have to deal with prototyping them.

Possible Solutions

So, how DO you use user defined types in your Arduino sketch?

Only use standard types

Well, the messy way is to avoid using functions that return or use those types. So, I could have altered my setState function to be

static void setState(int newState)
{
    /* Extra code omitted for clarity */
    currentState = (STATE_ENUM)newState;
}

This is a bit messy, as it hides the intent of the function input a bit.

Use a header file

Another option is to put the enumeration and typedef in a header file and #include it. The file is included before the auto-generated prototypes, so the compiler is happy.
This again is a bit messy, and not in the spirit of hiding implementation detail. If nothing else needs to know about your datatype, it shouldn’t be in a header file.

Hide the function from the auto-generator

Finally, the Arduino website build process page gives us a clue how we can solve this:

“Also, this generation isn’t perfect: it won’t create prototypes for functions that have default argument values, or which are declared within a namespace or class.”

So, we can make the function have a default argument:

static void setState(STATE_ENUM newState = STOPPED)
{
    /* Extra code omitted for clarity */
    currentState = newState;
}

…which will successfully compile, but now risks being called without an argument, which has the potential to do harmful things.

For the uber-paranoid, you could add an extra “invalid” value to the enumeration, make this the argument default, and test for this value right at the start of your function:

static void setState(STATE_ENUM newState = INVALID_STATE)
{
    if (newState == INVALID_STATE) { return; } // Do nothing if called without a state!
    /* Extra code omitted for clarity */
    currentState = newState;
}

The main disadvantage of this is that ALL the arguments after the defaulted one also need to have defaults. For example, this won’t compile:

static void setState(STATE_ENUM newState = INVALID_STATE, int anotherArg)

Because anotherArg needs a default value. This might be annoying.

The REAL solution

Ultimatley, the real solution is for the Arduino IDE to allow you to turn off automatic prototyping. Interestingly, an “Arduino for Visual Studio” application does exactly that.

There are LOADS of ways to do this, but I think a nice work-around would be a comment-style function decorator. Something like this:

static void setState(STATE_ENUM newState)/*NOPROTO*/

The Arduino build process would recognise the NOPROTO comment and suppress output of a prototype for that function.

This would avoid adding extra options to either the IDE or the preferences.txt file, and allow individual control of which functions are affected. A downside is that if you have a LOT of functions like this, the code gets littered with /*NOPROTO*/ everywhere.

What do I do?

Well, for the specific project above, I just modified the function to use a standard type.

I don’t tend to use the Arduino IDE much, so I think what I do in future will depend on the application.

Remember the mantra: “Software development IS decision making.” Get it tattoo’d on your eyeballs.

Advertisements
This entry was posted in Computing and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s