Function definition mistaken for variable declaration.

The program below seems to be valid C. It compiles with mingw gcc. But it produces lots of compile errors in the Arduino IDE. Can somebody point out what is wrong with the code?

relay_object_oriented_ish.ino (1.84 KB)

error_messages.txt (7.43 KB)

Part 1

#define RELAY_ON 1
#define RELAY_OFF 2

typedef struct{
  int outputPin;
  int state;
}RelayInfo;

void initRelay(RelayInfo *relay, int outputPin){
  relay->outputPin = outputPin;
  relay->state = RELAY_OFF;
  pinMode(outputPin, OUTPUT);
  digitalWrite(outputPin, LOW);
}

void flipRelay(RelayInfo *relay){
  if(relay->state == RELAY_OFF){
    digitalWrite(relay->outputPin, HIGH);
    relay->state = RELAY_ON;
  }
  else{
    digitalWrite(relay->outputPin, LOW);
    relay->state = RELAY_OFF;
  }
}

#define SWITCH_FREE 3
#define SWITCH_HELD 4

typedef struct{
  int inputPin;
  int state;
}SwitchInfo;

void initSwitch(SwitchInfo *sw, int inputPin){
  sw->inputPin = inputPin;
  sw->state = SWITCH_FREE;
  pinMode(inputPin, INPUT);
}

#define SWITCH_RELEASED 5
#define SWITCH_NOT_RELEASED 6

int checkSwitch(SwitchInfo *sw){
  if(digitalRead(sw->inputPin) == 0){
    if(sw->state == SWITCH_HELD){
      sw->state = SWITCH_FREE;
      return SWITCH_RELEASED;
    }
  }
  else if(sw->state == SWITCH_FREE){
    sw->state = SWITCH_HELD;
  }

  return SWITCH_NOT_RELEASED;
}

#define RELAY_ONE 12
#define RELAY_TWO 13

#define SWITCH_ONE 8
#define SWITCH_TWO 10

#define SWITCH_COUNT 2

RelayInfo relays[SWITCH_COUNT];
SwitchInfo switches[SWITCH_COUNT];

void setup() {
  initRelay(&relays[0], RELAY_ONE);
  initRelay(&relays[1], RELAY_TWO);
  
  initSwitch(&switches[0], SWITCH_ONE);
  initSwitch(&switches[1], SWITCH_TWO);
}

void loop() {
  int number = 0;
  
//  while(number <= SWITCH_COUNT){
//    if(checkSwitch(&switches[number]) == SWITCH_RELEASED){
//      flipRelay(&relays[number]);
//    }
//    number++;
//  }

  if(checkSwitch(&switches[0]) == SWITCH_RELEASED){
    flipRelay(&relays[0]);
  }

  if(checkSwitch(&switches[1]) == SWITCH_RELEASED){
    flipRelay(&relays[0]);
  }
}

Part 2

Arduino: 1.8.12 (Windows 10), Board: "Arduino Uno"

C:\Program Files (x86)\Arduino\arduino-builder -dump-prefs -logger=machine -hardware C:\Program Files (x86)\Arduino\hardware -tools C:\Program Files (x86)\Arduino\tools-builder -tools C:\Program Files (x86)\Arduino\hardware\tools\avr -built-in-libraries C:\Program Files (x86)\Arduino\libraries -libraries C:\Users\user\Documents\Arduino\libraries -fqbn=arduino:avr:uno -ide-version=10812 -build-path C:\Users\user\AppData\Local\Temp\arduino_build_958948 -warnings=all -build-cache C:\Users\user\AppData\Local\Temp\arduino_cache_68585 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avr-gcc.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino5.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.arduinoOTA.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.arduinoOTA-1.3.0.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avrdude.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -verbose E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino
C:\Program Files (x86)\Arduino\arduino-builder -compile -logger=machine -hardware C:\Program Files (x86)\Arduino\hardware -tools C:\Program Files (x86)\Arduino\tools-builder -tools C:\Program Files (x86)\Arduino\hardware\tools\avr -built-in-libraries C:\Program Files (x86)\Arduino\libraries -libraries C:\Users\user\Documents\Arduino\libraries -fqbn=arduino:avr:uno -ide-version=10812 -build-path C:\Users\user\AppData\Local\Temp\arduino_build_958948 -warnings=all -build-cache C:\Users\user\AppData\Local\Temp\arduino_cache_68585 -prefs=build.warn_data_percentage=75 -prefs=runtime.tools.avr-gcc.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avr-gcc-7.3.0-atmel3.6.1-arduino5.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.arduinoOTA.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.arduinoOTA-1.3.0.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avrdude.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -prefs=runtime.tools.avrdude-6.3.0-arduino17.path=C:\Program Files (x86)\Arduino\hardware\tools\avr -verbose E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino
Using board 'uno' from platform in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr
Using core 'arduino' from platform in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr
Detecting libraries used...
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10812 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino" "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\standard" "C:\\Users\\user\\AppData\\Local\\Temp\\arduino_build_958948\\sketch\\relay_object_oriented_ish.ino.cpp" -o nul
Generating function prototypes...
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10812 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino" "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\standard" "C:\\Users\\user\\AppData\\Local\\Temp\\arduino_build_958948\\sketch\\relay_object_oriented_ish.ino.cpp" -o "C:\\Users\\user\\AppData\\Local\\Temp\\arduino_build_958948\\preproc\\ctags_target_for_gcc_minus_e.cpp"
"C:\\Program Files (x86)\\Arduino\\tools-builder\\ctags\\5.8-arduino11/ctags" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "C:\\Users\\user\\AppData\\Local\\Temp\\arduino_build_958948\\preproc\\ctags_target_for_gcc_minus_e.cpp"
Compiling sketch...
"C:\\Program Files (x86)\\Arduino\\hardware\\tools\\avr/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10812 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino" "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\standard" "C:\\Users\\user\\AppData\\Local\\Temp\\arduino_build_958948\\sketch\\relay_object_oriented_ish.ino.cpp" -o "C:\\Users\\user\\AppData\\Local\\Temp\\arduino_build_958948\\sketch\\relay_object_oriented_ish.ino.cpp.o"
relay_object_oriented_ish:36:17: error: variable or field 'initSwitch' declared void

 void initSwitch(SwitchInfo *sw, int inputPin){

                 ^~~~~~~~~~

relay_object_oriented_ish:36:17: error: 'SwitchInfo' was not declared in this scope

relay_object_oriented_ish:36:29: error: 'sw' was not declared in this scope

 void initSwitch(SwitchInfo *sw, int inputPin){

                             ^~

E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino:36:29: note: suggested alternative: 'sq'

 void initSwitch(SwitchInfo *sw, int inputPin){

                             ^~

                             sq

relay_object_oriented_ish:36:33: error: expected primary-expression before 'int'

 void initSwitch(SwitchInfo *sw, int inputPin){

                                 ^~~

relay_object_oriented_ish:45:17: error: 'SwitchInfo' was not declared in this scope

 int checkSwitch(SwitchInfo *sw){

                 ^~~~~~~~~~

relay_object_oriented_ish:45:29: error: 'sw' was not declared in this scope

 int checkSwitch(SwitchInfo *sw){

                             ^~

E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino:45:29: note: suggested alternative: 'sq'

 int checkSwitch(SwitchInfo *sw){

                             ^~

                             sq

E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino: In function 'int checkSwitch(SwitchInfo*)':

relay_object_oriented_ish:45:31: error: 'int checkSwitch(SwitchInfo*)' redeclared as different kind of symbol

 int checkSwitch(SwitchInfo *sw){

                               ^

E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino:45:5: note: previous declaration 'int checkSwitch'

 int checkSwitch(SwitchInfo *sw){

     ^~~~~~~~~~~

E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino: In function 'void loop()':

relay_object_oriented_ish:88:30: error: 'checkSwitch' cannot be used as a function

   if(checkSwitch(&switches[0]) == SWITCH_RELEASED){

                              ^

relay_object_oriented_ish:92:30: error: 'checkSwitch' cannot be used as a function

   if(checkSwitch(&switches[1]) == SWITCH_RELEASED){

                              ^

E:\progg-tutorial\arduino\relay_object_oriented_ish\relay_object_oriented_ish.ino:79:7: warning: unused variable 'number' [-Wunused-variable]

   int number = 0;

       ^~~~~~

exit status 1
variable or field 'initSwitch' declared void

Just move the typedef a few lines up and it compiles… :slight_smile:

#define RELAY_ON 1
#define RELAY_OFF 2

typedef struct{
  int outputPin;
  int state;
}RelayInfo;

typedef struct{
  int inputPin;
  int state;
}SwitchInfo;

void initRelay(RelayInfo *relay, int outputPin){
  relay->outputPin = outputPin;
  relay->state = RELAY_OFF;
  pinMode(outputPin, OUTPUT);
  digitalWrite(outputPin, LOW);
}

void flipRelay(RelayInfo *relay){
  if(relay->state == RELAY_OFF){
    digitalWrite(relay->outputPin, HIGH);
    relay->state = RELAY_ON;
  }
  else{
    digitalWrite(relay->outputPin, LOW);
    relay->state = RELAY_OFF;
  }
}

#define SWITCH_FREE 3
#define SWITCH_HELD 4

void initSwitch(SwitchInfo *sw, int inputPin){
  sw->inputPin = inputPin;
  sw->state = SWITCH_FREE;
  pinMode(inputPin, INPUT);
}

#define SWITCH_RELEASED 5
#define SWITCH_NOT_RELEASED 6

int checkSwitch(SwitchInfo *sw){
  if(digitalRead(sw->inputPin) == 0){
    if(sw->state == SWITCH_HELD){
      sw->state = SWITCH_FREE;
      return SWITCH_RELEASED;
    }
  }
  else if(sw->state == SWITCH_FREE){
    sw->state = SWITCH_HELD;
  }

  return SWITCH_NOT_RELEASED;
}

#define RELAY_ONE 12
#define RELAY_TWO 13

#define SWITCH_ONE 8
#define SWITCH_TWO 10

#define SWITCH_COUNT 2

RelayInfo relays[SWITCH_COUNT];
SwitchInfo switches[SWITCH_COUNT];

void setup() {
  initRelay(&relays[0], RELAY_ONE);
  initRelay(&relays[1], RELAY_TWO);
  
  initSwitch(&switches[0], SWITCH_ONE);
  initSwitch(&switches[1], SWITCH_TWO);
}

void loop() {
  int number = 0;
  
//  while(number <= SWITCH_COUNT){
//    if(checkSwitch(&switches[number]) == SWITCH_RELEASED){
//      flipRelay(&relays[number]);
//    }
//    number++;
//  }

  if(checkSwitch(&switches[0]) == SWITCH_RELEASED){
    flipRelay(&relays[0]);
  }

  if(checkSwitch(&switches[1]) == SWITCH_RELEASED){
    flipRelay(&relays[0]);
  }
}

Another option:

struct SwitchInfo
{
  int inputPin;
  int state;
};


void initSwitch(struct SwitchInfo *sw, int inputPin)
{

The problem happens when an auto-generated function prototype using a named type is inserted before the declaration of the named type. Changing the 'typedef' to a named 'struct' type and specifying the 'struct' keyword in the function declaration (and thus the auto-generated prototype) resolves the problem.

Another option is to put manual prototypes in after the typedef so that the auto-prototypes are not generated for that function:

typedef struct 
{
  int inputPin;
  int state;
} SwitchInfo;


void initSwitch(SwitchInfo *sw, int inputPin);  // manual prototype
void initSwitch(SwitchInfo *sw, int inputPin)
{
  sw->inputPin = inputPin;
  sw->state = SWITCH_FREE;
  pinMode(inputPin, INPUT);
}


#define SWITCH_RELEASED 5
#define SWITCH_NOT_RELEASED 6

int checkSwitch(SwitchInfo *sw);  // For some reason, this manual prototype is now also required.
int checkSwitch(SwitchInfo *sw)
{

Thanks for all your replies. I will take up the manual prototypes method and add in prototypes for the initSwitch and checkSwitch functions.

But why is this not necessary for the initRelay and flipRelay functions? They seem to work fine without manual prototypes. They have the same kind of structure as the switch functions after all.

Having functions that take a struct pointer as a parameter led to the idea of C++ (classes).

So now you have a perfect, actual problem to see the advantages of classes. :wink:
or structs with functions which is basically the same.

enum {
  RELAY_ON = 1,
  RELAY_OFF,
  SWITCH_FREE,
  SWITCH_HELD,
  SWITCH_RELEASED,
  SWITCH_NOT_RELEASED,
};

struct RelayInfo {
  byte outputPin;
  byte state;
  RelayInfo(byte oPin) : outputPin(oPin), state(RELAY_OFF) {}
  void begin() {
    pinMode(outputPin, OUTPUT);
    digitalWrite(outputPin, LOW);
  }
  void flip() {
    if (state == RELAY_OFF) {
      state = RELAY_ON;
      digitalWrite(outputPin, HIGH);
    } else {
      state = RELAY_OFF;
      digitalWrite(outputPin, LOW);
    }
  }
};

struct SwitchInfo {
  byte inputPin;
  byte state;
  SwitchInfo(byte iPin) : inputPin(iPin), state(SWITCH_FREE) {}
  void begin() {
    pinMode(inputPin, INPUT);
  }
  int check() {
    if (!digitalRead(inputPin)) {
      if (state == SWITCH_HELD) {
        state = SWITCH_FREE;
        return SWITCH_RELEASED;
      }
    } else {
      if (state == SWITCH_FREE) {
        state = SWITCH_HELD;
      }
    }
    return SWITCH_NOT_RELEASED;
  }
};

RelayInfo relays[] = { 12, 13 };
SwitchInfo switches[] = { 8, 10 };

void setup() {
  relays[0].begin();
  relays[1].begin();
  switches[0].begin();
  switches[1].begin();
}

void loop() {
  for (byte i = 0; i < sizeof(relays) / sizeof(relays[0]); i++) {
    if (switches[i].check() == SWITCH_RELEASED) {
      relays[i].flip();
    }
  }
}

Do you really have external pulldowns? Do your relays really switch on with HIGH?

sreelesh77:
But why is this not necessary for the initRelay and flipRelay functions? They seem to work fine without manual prototypes. They have the same kind of structure as the switch functions after all.

It all depends on where the auto-generated prototypes are generated relative to the type definitions they rely on.. I think they aim to put them just before the first function declaration in the file. This puts them AFTER the declaration of "typedef struct {...} RelayInfo;" and before "typedef struct {...} SwitchInfo;". At that point RelayInfo is a known type so the prototypes that use RelayInfo are fine but SwitchInfo is not yet defined so the ones that use SwitchInfo fail.

One simple fix is to put the struct definitions in an include file, and #include that file near the top of your .ino file. The IDE will put its auto-generated prototypes after all the #includes.

Regards,
Ray L.