3

I have a program that typedef's two structs. Each one has a corresponding function that returns a pointer to it....

typedef struct {
  int x1;
} x;

x *a;

x *f(void) {
  return a;    
}

typedef struct {
    int  y1;
} y;


y *b;     

y *g(void) {
  return b;
}


void loop(void) {

}

void setup(void) {

}

This program produces the following error when compiled under the Arduino IDE...

sketch_nov10a:18: error: 'y' does not name a type

 y *g(void)

If we copy/paste to switch the order of the two blocks like so...

typedef struct {
    int  y1;
} y;


y *b;     

y *g(void) {
  return b;
}

typedef struct {
  int x1;
} x;

x *a;

x *f(void) {
  return a;    
}

void loop(void) {

}

void setup(void) {

}

...then we instead get the following error...

sketch_nov10a:18: error: 'x' does not name a type

x *f(void)

...so apparently the Arduino IDE only supports functions that return pointers to the first typedef'ed type defined in the file?

Is there a rational to this? How would you implement the above code to get it to compile under the Arduino IDE (it works fine in gcc).

3 Answers 3

5

The Arduino IDE, rather annoyingly, tries to "help" you by generating function prototypes for functions, something it sometimes fails to do.

You can make your code compile by adding one yourself:

typedef struct {
  int x1;
} x;

x *a;

x *f(void) {
  return a;    
}

typedef struct {
    int  y1;
} y;


y *b;     

y *g(void);    // <------------------ ADD THIS
y *g(void) {
  return b;
}


void loop(void) {

}

void setup(void) {

}

I have a post about how to avoid the quirks in the IDE which may help you.

If you just move your code into a separate .CPP file and leave the main sketch blank, it will compile, if you add this line to the start of the .CPP file:

#include <Arduino.h>

The Arduino IDE uses gcc, so it isn't a problem with the compiler exactly, it is a problem with how it "helps" you.

3
  • This is the correct answer and solves the problem, but so unsatisfyingly... :/ Thanks!
    – bigjosh
    Nov 12, 2017 at 5:31
  • but so unsatisfyingly - yes, I know. In order to save teaching newbies about function prototypes they have created a monster that behaves in a way that confuses non-newbies. :)
    – Nick Gammon
    Nov 12, 2017 at 6:10
  • I tracked this down and I'd classify it as a logic bug in the arduino-builder tool. See this new answer for details.
    – bigjosh
    Nov 12, 2017 at 22:20
1

Cause

Ok, I've pinned down the root of this issue.

The arduino-builder program is responsible for mangling the sketch INO file into a cpp file to be fed into gcc. Among other things, it scans the file for function definitions and then adds prototypes for all the found functions.

The prototypes are added as a block just before the first function declaration in the file.

Unfortunately this strategy fails any time there is the following sequence....

  1. a function definition
  2. a type definition
  3. a function definition that depends on the type defined in #2

...since the prototype for the function defined in #3 ends up being added before the type definition in #2 on which it depends, creating the compilation error.

The code responsible for this behavior is here...

https://github.com/arduino/arduino-builder/blob/b91cea8c8a42803b0b40dcdde2d1165c4e225457/src/arduino.cc/builder/ctags_runner.go#L83

A more robust strategy would be to add each function prototype before the first function definition that calls the function being prototyped.

Workaround

In the meantime, a workaround is to move all type definitions to be before the first function definition in the source code.

Rearranging the above example program like so...

typedef struct {
  int x1;
} x;

typedef struct {
    int  y1;
} y;



x *a;

x *f(void) {
  return a;    
}


y *b;     

y *g(void) {
  return b;
}


void loop(void) {

}

void setup(void) {

}

...resolves the error, at the expense of code clarity.

1
  • It would be hard to get the builder to work in all circumstances (for example if you use #define to obscure your intent). Effectively it needs to know what the compiler knows, so effectively they need to turn it into a compiler to do the job properly. Your suggestion works (as does mine) but this is not something people normally expect to have to do.
    – Nick Gammon
    Nov 13, 2017 at 6:05
0

bigjosh, Thank you for a small and working sketch that shows the problem !

It is probably a bug of the Arduino preprocessing before handing it over to the compiler, as @nick-gammon wrote.

When I rewrite the code in my own way (in a normal sketch, a *.ino file), the error message is gone. I use a more modern style of the struct typedefs, and I declare first the typedefs and after that the variables and after that the functions.
I suppose that when multiple include files are used, then this might not be possible.

// declare the typedefs.
struct x {
  int x1;
};

struct y {
  int  y1;
};

// create variables (pointers)
x *a;
y *b;     

// Arduino functions
void loop(void) {
}

void setup(void) {
}

// Your own functions
x *f(void) {
  return a;    
}

y *g(void) {
  return b;
}

I hope the data of the struct is located somewhere, because the sketch has only pointers and no valid memory location that they can point to.

3
  • You wrote x and y struct definitions, rather than typedefs. That approach is not "newer" than that in the question; if anything, it's older. Note, the code you show shouldn't compile correctly unless you add struct before x *a; and y *b;. See the first answer to Difference between 'struct' and 'typedef struct' in C++?, Nov 11, 2017 at 20:39
  • @JamesWaldby-jwpat7 Did you try to compile it ? I'm using the C++ way where the struct/union/enum create a new type. I understand that it can cause a problem, when a similar name gets hidden. That's about how far my knowledge stretches. It is not wrong, and I consider it even as normal C++ programming. I'm happy to learn new things though.
    – Jot
    Nov 12, 2017 at 2:03
  • Ignore the “shouldn't compile correctly” part of my comment (which is true in C not C++, as I noticed too late to edit) . It might be worthwhile to ignore the whole comment :) but I'll leave it there for the link. Nov 12, 2017 at 2:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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