My class/reference won't work in the sketch



A very frequent error for Arduino users occurs when a sketch defines a class, struct or typedef and uses it as a parameter to a function. This error does not affect all classes, structs and typedefs however, only the ones defined in the actual sketch file cause problems explained here.

The error stems back to the way the IDE modifies your code before its compiled. For a detailed look at what the IDE does change, the article 'What does the IDE change in my sketch' might provide some insight.

A common work around for this problem is to create a separate header file for the class declaration, which may also need a cpp file for the definition. While this method delivers a working solution, there are ways to achieve the result using only the sketch file.

The snippet below shows a typical failure.

struct MyStruct{
    bool Value;
};

void Invert( MyStruct &m ){
  m.Value ^= m.Value;
}

void setup(){
  MyStruct m_Temp = { true };
  Invert( m_Temp );
}

void loop(){}

The next snippet is the source code once the IDE has modified it.

#line 1 "reference_fail.ino"

#include "Arduino.h"
void Invert( MyStruct &m );
void setup();
void loop();
#line 2
struct MyStruct{
    bool Value;
};

void Invert( MyStruct &m ){
  m.Value ^= m.Value;
}

void setup(){
  MyStruct m_Temp = { true };
  Invert( m_Temp );
}

void loop(){}

The snippet above shows how the generated function prototypes have been placed above the class definition. This is similar to the #ifdef problem in reference to the generated code being placed above the first source line. As a result of the modifications, the struct is used in the reference declaration before the class has actually been defined. This is enforced by the way C++ compiles its sources from top to bottom.

There is a misconception that this error affects reference parameters only, however the error will manifest itself with a parameter passed by value and also function return types of the user defined class. The error does not occur on local function variables (declared inside a function).

One straight forward way to fix this error is to manually add the function prototype in a valid location below the structure. Please note however, the IDE requires this prototype to be copied character for character plus the terminating semi-colon, an extra space or two inside the declaration will cause the IDE to add an additional prototype above the structure. If deemed a match, the IDE will not generate a new prototype. The code below shows the working solution to the above problem.

struct MyStruct{
    bool Value;
};

//Manually added prototype.
void Invert( MyStruct &m );

void Invert( MyStruct &m ){
  m.Value ^= m.Value;
}

void setup(){
  MyStruct m_Temp = { true };
  Invert( m_Temp );
}

void loop(){}

If you are constantly changing the prototypes while developing your program, manually updating each prototype may not be the most favourable solution. If this is the case with you, I have discovered a working alternative which allows you to prevent a prototype from being generated for your function and there is no need to provide your own. Skip the next two paragraphs if you want an answer without  the sugar coating Cool.

As a programmer coming from a main stream C++ environment, I frequently use a feature of C++ called exceptions, or exception handling. This feature is not available under standard AVR and Arduino installations as it has been decided that the overhead outweighed its advantages.

However there is a feature called exception specifications which can be utilized, and compilers simply ignore these specifications if exceptions happen to be disabled. A programmer adds specifications to functions to show other users of the code, and the compiler what sort of exception the function may throw. There is even a specification which says a function will not throw an exception, and that is what we are going to use.

A small snippet below replaces the sketch above using a no-throw exception specification. This is done by adding throw() to the end of the declaration, but before the opening code bracket.

struct MyStruct{
    bool Value;
};

void Invert( MyStruct &m ) throw(){
  m.Value ^= m.Value;
}

void setup(){
  MyStruct m_Temp = { true };
  Invert( m_Temp );
}

void loop(){}

Please take note, the function now has no prototype generated. This sketch works because Invert() is called from setup() which is located after Invert(). If Invert() happened to be located after setup(), an error would occur becuase Invert() has not been seen (defined) yet. The order of your code is important, but that extends to the order of functions when a prototype is not being used.

This use of the throw() specification works due to the way the IDE searches for function prototypes. To sum up the operation, the Arduino IDE uses regular expressions to match a pattern consisting of any number of words, a parameter list and finally a closing parentheses ) which preceeds an opening curly bracket {. The throw() disrupts this pattern and the IDE ignores the function declaration.

Thanks for reading, please comment if you have any further queries.

Tags: class, reference, struct, typedef, undefined
Last update:
2014-04-10 16:07
Author:
Christopher Andrews
Revision:
1.1
Average rating: 5 (1 Vote)

You can comment this FAQ

Chuck Norris has counted to infinity. Twice.