I am writing an Arduino library with a circular dependency.
A similar circular dependency works when compiled with gcc.
But the Arduino IDE gets an error. Verbose error message is:
Arduino: 1.6.7 (Linux), TD: 1.27, Board: "Teensy 2.0, Serial, 16 MHz, US English"
/opt/arduino-1.6.7/arduino-builder -dump-prefs -logger=machine -hardware "/opt/arduino-1.6.7/hardware" -tools "/opt/arduino-1.6.7/tools-builder" -tools "/opt/arduino-1.6.7/hardware/tools/avr" -built-in-libraries "/opt/arduino-1.6.7/libraries" -libraries "/home/wolfv/Documents/Arduino/libraries" -fqbn=teensy:avr:teensy2:usb=serial,speed=16,keys=en-us -ide-version=10607 -build-path "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp" -warnings=all -prefs=build.warn_data_percentage=75 -verbose "/home/wolfv/Documents/Arduino/demo/circular/circular.ino"
/opt/arduino-1.6.7/arduino-builder -compile -logger=machine -hardware "/opt/arduino-1.6.7/hardware" -tools "/opt/arduino-1.6.7/tools-builder" -tools "/opt/arduino-1.6.7/hardware/tools/avr" -built-in-libraries "/opt/arduino-1.6.7/libraries" -libraries "/home/wolfv/Documents/Arduino/libraries" -fqbn=teensy:avr:teensy2:usb=serial,speed=16,keys=en-us -ide-version=10607 -build-path "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp" -warnings=all -prefs=build.warn_data_percentage=75 -verbose "/home/wolfv/Documents/Arduino/demo/circular/circular.ino"
"/opt/arduino-1.6.7/hardware/tools/avr/../avr/bin/avr-g++" -E -CC -x c++ -w -g -Wall -ffunction-sections -fdata-sections -fno-exceptions -felide-constructors -std=c++0x -mmcu=atmega32u4 -DTEENSYDUINO=127 -DARDUINO=10607 -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-I/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy" "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/circular.ino.cpp" -o "/dev/null"
"/opt/arduino-1.6.7/hardware/tools/avr/../avr/bin/avr-g++" -E -CC -x c++ -w -g -Wall -ffunction-sections -fdata-sections -fno-exceptions -felide-constructors -std=c++0x -mmcu=atmega32u4 -DTEENSYDUINO=127 -DARDUINO=10607 -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-I/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy" "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp" -o "/dev/null"
"/opt/arduino-1.6.7/hardware/tools/avr/../avr/bin/avr-g++" -E -CC -x c++ -w -g -Wall -ffunction-sections -fdata-sections -fno-exceptions -felide-constructors -std=c++0x -mmcu=atmega32u4 -DTEENSYDUINO=127 -DARDUINO=10607 -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-I/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy" "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/BClass.cpp" -o "/dev/null"
"/opt/arduino-1.6.7/hardware/tools/avr/../avr/bin/avr-g++" -E -CC -x c++ -w -g -Wall -ffunction-sections -fdata-sections -fno-exceptions -felide-constructors -std=c++0x -mmcu=atmega32u4 -DTEENSYDUINO=127 -DARDUINO=10607 -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-I/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy" "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/circular.ino.cpp" -o "/dev/null"
"/opt/arduino-1.6.7/hardware/tools/avr/../avr/bin/avr-g++" -E -CC -x c++ -w -g -Wall -ffunction-sections -fdata-sections -fno-exceptions -felide-constructors -std=c++0x -mmcu=atmega32u4 -DTEENSYDUINO=127 -DARDUINO=10607 -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-I/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy" "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/circular.ino.cpp" -o "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/preproc/ctags_target_for_gcc_minus_e.cpp"
"/opt/arduino-1.6.7/tools-builder/ctags/5.8-arduino5/ctags" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/preproc/ctags_target_for_gcc_minus_e.cpp"
"/opt/arduino-1.6.7/hardware/tools/avr/../avr/bin/avr-g++" -c -Os -g -Wall -ffunction-sections -fdata-sections -MMD -fno-exceptions -felide-constructors -std=c++0x -mmcu=atmega32u4 -DTEENSYDUINO=127 -DARDUINO=10607 -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-I/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy" "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp" -o "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp.o"
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:1:6: error: 'AClass' has not been declared
void AClass::initialize(BClass* ptr) { ptrBClass = ptr; }
^
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:1:25: error: variable or field 'initialize' declared void
void AClass::initialize(BClass* ptr) { ptrBClass = ptr; }
^
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:1:25: error: 'BClass' was not declared in this scope
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:1:33: error: 'ptr' was not declared in this scope
void AClass::initialize(BClass* ptr) { ptrBClass = ptr; }
^
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:2:6: error: 'AClass' has not been declared
void AClass::printB() { ptrBClass->print(); }
^
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp: In function 'void printB()':
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:2:25: error: 'ptrBClass' was not declared in this scope
void AClass::printB() { ptrBClass->print(); }
^
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp: At global scope:
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:3:6: error: 'AClass' has not been declared
void AClass::print() { Serial.print("a"); }
^
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp: In function 'void print()':
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:3:24: error: 'Serial' was not declared in this scope
void AClass::print() { Serial.print("a"); }
^
exit status 1
'Serial' was not declared in this scope
circular.ino:
#include "AClass.h"
#include "BClass.h"
/* .cpp files are included here because of circular dependency.
BClass has refAClass. AClass has ptrBClass.
Both class definitions need both class declarations.
Preprocessor should copy .h and .cpp files of both classes together into one file.
*/
#include "AClass.cpp"
#include "BClass.cpp"
AClass aObject;
BClass bObject(aObject); //start circular dependency
void setup()
{
aObject.initialize(&bObject); //close the circular dependency
aObject.printB();
bObject.printA();
}
void loop() { }
AClass.h:
#ifndef ACLASS_H
#define ACLASS_H
#include <Arduino.h>
class BClass;
/*
initialize() should be called once in setup.
*/
class AClass
{
private:
BClass* ptrBClass;
public:
void initialize(BClass* ptr);
void printB();
void print();
};
#endif
The Arduino IDE generates an ctags_target_for_gcc_minus_e.cpp file located at:
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/preproc/ctags_target_for_gcc_minus_e.cpp
The ctags_target_for_gcc_minus_e.cpp file shows the included files in the expected order.
I copied the last 62 lines of ctags_target_for_gcc_minus_e.cpp file into an .ino file and it works as expected:
/*
initialize() should be called once in setup.
*/
class AClass
{
private:
BClass* ptrBClass;
public:
void initialize(BClass* ptr);
void printB();
void print();
};
# 5 "/home/wolfv/Documents/Arduino/demo/circular/circular.ino" 2
# 1 "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/BClass.h" 1
# 1 "/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy/Arduino.h" 1
# 5 "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/BClass.h" 2
class AClass;
class BClass
{
private:
AClass& refAClass;
public:
BClass(AClass& refAClass);
void printA();
void print();
};
# 6 "/home/wolfv/Documents/Arduino/demo/circular/circular.ino" 2
/* .cpp files are included here because of circular dependency.
BClass has refAClass. AClass has ptrBClass.
Both class definitions need both class declarations.
Preprocessor should copy .h and .cpp files of both classes together into one file.
*/
# 1 "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp" 1
void AClass::initialize(BClass* ptr) { ptrBClass = ptr; }
void AClass::printB() { ptrBClass->print(); }
void AClass::print() { Serial.print("a"); }
# 13 "/home/wolfv/Documents/Arduino/demo/circular/circular.ino" 2
# 1 "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/BClass.cpp" 1
BClass::BClass(AClass& refAClass) : refAClass(refAClass) { }
void BClass::printA() { refAClass.print(); }
void BClass::print() { Serial.print("b"); }
# 14 "/home/wolfv/Documents/Arduino/demo/circular/circular.ino" 2
AClass aObject;
BClass bObject(aObject); //start circular dependency
void setup()
{
aObject.initialize(&bObject); //close the circular dependency
aObject.printB();
bObject.printA();
}
void loop() { }
output:
ba
Why would the Arduino IDE have an error when the ctags_target_for_gcc_minus_e.cpp file code works just fine?
How to make the circular dependency work in Arduino with #include files?
Interesting. I am surprised that compiles on 1.0.5.
There are no #include in the .cpp files.
Removing the #include .cpp still got the exact same error in Arduino 1.6.7.
But the ctags_target_for_gcc_minus_e.cpp file is now missing the header files AClass.h and BClass.h:
class BClass;
/*
initialize() should be called once in setup.
*/
class AClass
{
private:
BClass* ptrBClass;
public:
void initialize(BClass* ptr);
void printB();
void print();
};
# 2 "/home/wolfv/Documents/Arduino/demo/circular/circular.ino" 2
# 1 "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/BClass.h" 1
# 1 "/opt/arduino-1.6.7/hardware/teensy/avr/cores/teensy/Arduino.h" 1
# 5 "/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/BClass.h" 2
class AClass;
class BClass
{
private:
AClass& refAClass;
public:
BClass(AClass& refAClass);
void printA();
void print();
};
# 3 "/home/wolfv/Documents/Arduino/demo/circular/circular.ino" 2
/* .cpp files are included here because of circular dependency.
BClass has refAClass. AClass has ptrBClass.
Both class definitions need both class declarations.
Preprocessor should copy .h and .cpp files of both classes together into one file.
*/
//#include "AClass.cpp"
//#include "BClass.cpp"
AClass aObject;
BClass bObject(aObject); //start circular dependency
void setup()
{
aObject.initialize(&bObject); //close the circular dependency
aObject.printB();
bObject.printA();
}
void loop() { }
The Arduino 1.6.7 generates a ctags_target_for_gcc_minus_e.cpp file located at:
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/preproc/ctags_target_for_gcc_minus_e.cpp
PaulS:
Yeah, well, I had to add them. Surely, you do not expect magic to happen.
And I have no idea what you mean about the "ctags_target_for_gcc_minus_e.cpp file".
For all I know it could have been Arduino magic
After adding the #include header files to the .cpp files, Arduino 1.6.7 gets this error message:
I copied the files into 1.6.6 (the latest version I have), added the #include statements to the cpp files, removed the #include statements that included the cpp files in the ino file, and I get just one "error":
Sketch uses 4,404 bytes (15%) of program storage space. Maximum is 28,672 bytes.
Global variables use 159 bytes (6%) of dynamic memory, leaving 2,401 bytes for local variables. Maximum is 2,560 bytes.
I installed arduino-1.6.6-linux64.tar, edited #include statements as you describe, and get the compile error:
Arduino: 1.6.5 (Linux), Board: "Arduino Leonardo"
/usr/local/bin/arduino-1.6.5/hardware/tools/avr/bin/avr-g++ -c -g -Os -Wall -Wextra -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega32u4 -DF_CPU=16000000L -DARDUINO=10605 -DARDUINO_AVR_LEONARDO -DARDUINO_ARCH_AVR -DUSB_VID=0x2341 -DUSB_PID=0x8036 -DUSB_MANUFACTURER="Unknown" -DUSB_PRODUCT="Arduino Leonardo" -I/usr/local/bin/arduino-1.6.5/hardware/arduino/avr/cores/arduino -I/usr/local/bin/arduino-1.6.5/hardware/arduino/avr/variants/leonardo /tmp/build7494658067615920056.tmp/BClass.cpp -o /tmp/build7494658067615920056.tmp/BClass.cpp.o
/tmp/build7494658067615920056.tmp/BClass.cpp: In member function 'void BClass::printA()':
/tmp/build7494658067615920056.tmp/BClass.cpp:4:34: error: invalid use of incomplete type 'class AClass'
void BClass::printA() { refAClass.print(); }
^
In file included from /tmp/build7494658067615920056.tmp/BClass.cpp:1:0:
/tmp/build7494658067615920056.tmp/BClass.h:6:7: error: forward declaration of 'class AClass'
class AClass;
^
Error compiling.
Could you please check to see if they following example matches your working copy?
Sorry to put you through so much trouble. I don’t know what else to try.
circular.ino
#include "AClass.h"
#include "BClass.h"
/* .cpp files are included here because of circular dependency.
BClass has refAClass. AClass has ptrBClass.
Both class definitions need both class declarations.
Preprocessor should copy .h and .cpp files of both classes together into one file.
*/
//#include "AClass.cpp"
//#include "BClass.cpp"
AClass aObject;
BClass bObject(aObject); //start circular dependency
void setup()
{
aObject.initialize(&bObject); //close the circular dependency
aObject.printB();
bObject.printA();
}
void loop() { }
AClass.h
#ifndef ACLASS_H
#define ACLASS_H
#include <Arduino.h>
class BClass;
/*
initialize() should be called once in setup.
*/
class AClass
{
private:
BClass* ptrBClass;
public:
void initialize(BClass* ptr);
void printB();
void print();
};
#endif
Could you please check to see if they following example matches your working copy?
No, they don't. BClass.cpp needs to #include AClass.h, too, because in the BClass.h file, you've simply said that AClass is a class, without defining any more about it. So, as it is now, in BClass.cpp, the compiler only knows that AClass is a class - nothing more, which is what it is telling you.
#ifndef ACLASS_H
#define ACLASS_H
#include <Arduino.h>
class BClass;
/*
initialize() should be called once in setup.
*/
class AClass
{
private:
BClass* ptrBClass;
public:
void initialize(BClass* ptr);
void printB();
void print();
};
#endif