Help with syntax, please! Workaround for Struct issue.

I have been tearing my hair out :0 on this one all day. I am having problems finding the exact syntax for a struct passed to a function, and actually get my friggin' sketch to compile. I have been digging thru these forums and some C++ forums and followed all the examples precisely but it won't compile.

I've seen several different examples of structs, with different syntaxes, but none of them will compile when I have a function that uses it. I do need to be able to pass the struct to a function. And in the future, I may want the ability to return it. Apparently this problem is due to the autogeneration of prototypes. Some of the workarounds I've read say I need to put it into an .h file, or .cpp, or .pde, which I have tried. I've also tried turning it into a class without any luck. I've tried to pass it with a pointer but couldn't figure out a working syntax for that either.

I trimmed my project down to a simple test sketch, below. As posted, it will compile. But if you unremark the isHoliday() function, it returns an error: 'dateTime' was not declared in this scope and several others starting with the definition of the struct.

#define myBirthday 7
#define Christmas 5
#define NewYears 11

/*
struct dateTime {
  byte month;
  byte dayOfMonth;
  int year;
};

dateTime curDT = {6,22,1963};
// The code above compiles IF I get rid of the isHoliday() function
// but gives error: 'dateTime' was not declared in this scope
// on line 8 with the isHoliday() function unremarked

/*
// These don't work, gives error: 'curDT' does not name a type
// so how do I get or alter a value from one of it's elements?
//curDT.Month = 8;
//curDT->month = 8;
*/


//Also tried it as a class, like this:
class dateTime {
  public:
  byte month;
  byte dayOfMonth;
  int year;
  dateTime();
};

dateTime::dateTime() {
  this->month = 1;
  this->dayOfMonth = 1;
  this->year = 1970;
}

dateTime curDT();
/*
// These don't work with the class either, also gives error: 'curDT' does not name a type
// so how do I get or alter a value from one of it's elements?
//curDT.Month = 8;
//curDT->month = 8;
*/

int holidayList[] = { 0,0,0,0,0,0,0,0,0,0 };  // Maximum of 10 holidays or events on a single day
int holidayCount = 0;



void setup()
{
  // put your setup code here, to run once:
  Serial.begin(115200);
  
} // void setup

void loop()
{
  // put your main code here, to run repeatedly:
  int result;
  //result = isHoliday(curDT, holidayList);
  
  Serial.print ("Holiday Count = ");
  Serial.println (holidayCount);
  Serial.print ("Holidays: ");
  for (int i=0; i<holidayCount; i++) {
    Serial.print (holidayList[i]);
    Serial.print (", ");
  } // for holiday loop

} // void loop()

/*
int isHoliday(dateTime dt, int holidays[])
// causes error: 'dateTime' was not declared in this scope
// on line 8 above with the isHoliday() function not remarked out
{
  int numHolidays=0;
  addHoliday(holidays, numHolidays, myBirthday);
  addHoliday(holidays, numHolidays, Christmas);
  return numHolidays;
} // int isHoliday
*/

boolean addHoliday(int holidays[], int &numHolidays, int newHoliday)
{
  if (numHolidays < 10) {
    holidays[numHolidays] = newHoliday;
    numHolidays++;
    return true;
  } // if (curCount < 10)
  else {
    return false;
  } // else if (curCount < 10)  
} // boolean addHoliday

My real struct is much larger, with more elements, so it is impractical to pass them all as separate parameters to the function.
The isHoliday() function is also much more complex, checking the elements of the structure for a match.

P.S. I'm a C# programmer by day. Just similar enough, and yet just different enough to drive me nuts. ]:slight_smile:

This is what the IDE generates from your code before compiling it (your function not commented out):

#line 1 "sketch_mar29a.ino"
#define myBirthday 7
#define Christmas 5
#define NewYears 11

/*
struct dateTime {
  byte month;
  byte dayOfMonth;
  int year;
};

dateTime curDT = {6,22,1963};
// The code above compiles IF I get rid of the isHoliday() function
// but gives error: 'dateTime' was not declared in this scope
// on line 8 with the isHoliday() function unremarked

/*
// These don't work, gives error: 'curDT' does not name a type
// so how do I get or alter a value from one of it's elements?
//curDT.Month = 8;
//curDT->month = 8;
*/


//Also tried it as a class, like this:
#include "Arduino.h"
void setup();
void loop();
int isHoliday(dateTime dt, int holidays[]);
boolean addHoliday(int holidays[], int &numHolidays, int newHoliday);
#line 26
class dateTime {
  public:
  byte month;
  byte dayOfMonth;
  int year;
  dateTime();
};

dateTime::dateTime() {
  this->month = 1;
  this->dayOfMonth = 1;
  this->year = 1970;
}

dateTime curDT();
/*
// These don't work with the class either, also gives error: 'curDT' does not name a type
// so how do I get or alter a value from one of it's elements?
//curDT.Month = 8;
//curDT->month = 8;
*/

int holidayList[] = { 0,0,0,0,0,0,0,0,0,0 };  // Maximum of 10 holidays or events on a single day
int holidayCount = 0;



void setup()
{
  // put your setup code here, to run once:
  Serial.begin(115200);
  
} // void setup

void loop()
{
  // put your main code here, to run repeatedly:
  int result;
  //result = isHoliday(curDT, holidayList);
  
  Serial.print ("Holiday Count = ");
  Serial.println (holidayCount);
  Serial.print ("Holidays: ");
  for (int i=0; i<holidayCount; i++) {
    Serial.print (holidayList[i]);
    Serial.print (", ");
  } // for holiday loop

} // void loop()


int isHoliday(dateTime dt, int holidays[])
// causes error: 'dateTime' was not declared in this scope
// on line 8 above with the isHoliday() function not remarked out
{
  int numHolidays=0;
  addHoliday(holidays, numHolidays, myBirthday);
  addHoliday(holidays, numHolidays, Christmas);
  return numHolidays;
} // int isHoliday


boolean addHoliday(int holidays[], int &numHolidays, int newHoliday)
{
  if (numHolidays < 10) {
    holidays[numHolidays] = newHoliday;
    numHolidays++;
    return true;
  } // if (curCount < 10)
  else {
    return false;
  } // else if (curCount < 10)  
} // boolean addHoliday

You can avoid that if you separate your code and move the declaration of the dateTime class to a file "dateTime.h", the definition of the constructor to "dateTime.cpp" (where you include "dateTime.h") and put an include "dateTime.h" at the top of your main sketch.

You can also use the struct but remember that a struct is always called by its full name. So if you defined the struct as in the comments at the top of your code, you have to use it as "struct dateTime" and not just "dateTime".

DrWizard:
Apparently this problem is due to the autogeneration of prototypes.

Yeah, unfortunately the people who implemented the IDE weren't quite as clever as they thought - the automatic prototyping is a questionable idea implemented poorly and routinely messes up code like this. The simplest workaround for this bug in the IDE is to put your type definitions in a separate .h file and #include them in the .ino file. An alternative workaround is to put your code in ordinary .c/.cpp files instead of .ino files - only the .ino files benefit from this daft mucking about that the IDE does. There are some other workarounds that consist of putting the prototypes in for yourself (for some IDE versions, this will inhibit the IDE from adding its own ones) or adding a dummy variable declaration at the point where you want the autogenerated prototypes to appear.

You have my sympathy. I must have wasted an hour or more last night before I took the trouble to Google "Arduino struct" and found out about the need for a .h file from the Playground article.

[ rant]
I really detest the way C/C++ requires this sort of fragmentation of the program code. I was trying to figure out the logic of another program and I was constantly jumping between three different files.

I don't mind having different parts of the logic in different files as that generally makes the files shorter and easier to follow. But having to put different parts of the same piece of logic in different files is ridiculous. [ /rant].

...R

pylon:
You can also use the struct but remember that a struct is always called by its full name. So if you defined the struct as in the comments at the top of your code, you have to use it as "struct dateTime" and not just "dateTime".

Take note that Arduino uses C++, what you mention is only a C requirement, not C++ which only requires struct on the declaration, and there is only one rare circumstance where it is required elsewhere.

@OP, I have a few articles on the subject here which may provide more insight to Arduino's modifications.
http://arduino.land/FAQ/content/1/3/en/what-does-the-ide-change-in-my-sketch.html
http://arduino.land/FAQ/content/2/13/en/my-class_reference-won_t-work-in-the-sketch.html
http://arduino.land/FAQ/content/2/12/en/why-do-#ifdef-macro's-stop-the-sketch-from-compiling.html

I tried putting the struct definition in an .h file and #including the .h file, I swear! :fearful: But that created a different problem. Then I got
error: 'byte' does not name a type for each member of the structure (and yes, I #included <stdint.h> and <stdbool.h>)

I found a simple example on StackOverflow: Arduino: struct pointer as function parameter - Stack Overflow

My struct definition was fine, and I was able to leave it in my main .ino file. But as pylon mentioned above, I needed to put "struct dateTime" instead of just "dateTime" in my function declaration. I also needed to use a pointer, and not by reference (or value)

So my function became:int isHoliday(struct dateTime *dt, int holidays[]) and I called it with:holidayCount = isHoliday(&curDT, holidayList); sending it by reference.
Then in my isHoliday function, I had to access the members of dateTime using "->" instead of "."

My remaining problem of not being able to initialize the members, I solved by accident by moving that code to within the setup() procedure instead of before it, at the top.

Thanks for everyone's help!

and yes, I #included <stdint.h> and <stdbool.h>

Why would you do that, when byte is defined in Arduino.h?

I also needed to use a pointer, and not by reference

No.