How to resolve circular dependency?

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

AClass.cpp

void AClass::initialize(BClass* ptr) { ptrBClass = ptr; }
void AClass::printB() { ptrBClass->print(); }
void AClass::print() { Serial.print("a"); }

BClass.h

#ifndef BCLASS_H
#define BCLASS_H

#include <Arduino.h>

class AClass;

class BClass
{
    private:
        AClass& refAClass;
    public:
        BClass(AClass& refAClass);
        void printA();
        void print();
};
#endif

BClass.cpp

BClass::BClass(AClass& refAClass) : refAClass(refAClass) { }
void BClass::printA() { refAClass.print(); }
void BClass::print() { Serial.print("b"); }

(continued)

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?

Thank you.

Use the #ifdef trick

http://c-faq.com/cpp/nestincl.html

Hi KeithRB.

The example code uses header guards as you suggested.

Put a dummy #define as the first line of your file. The IDE can get confused when munging files.

I put a “#define DUMMY” at the top of each file, but unfortunately it did not make a difference.

#include "AClass.cpp"
#include "BClass.cpp"

Do NOT include cpp files in .ino files!

Once I removed these, your code compiled just file on 1.0.5.

Hi PaulS,

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() { }

There are no #include in the .cpp files.

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”.

Thanks PaulS.

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 :slight_smile:

After adding the #include header files to the .cpp files, Arduino 1.6.7 gets this error message:

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: In member function 'void AClass::printB()':
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:4:34: error: invalid use of incomplete type 'class BClass'
void AClass::printB() { ptrBClass->print(); }
^
In file included from /tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.cpp:1:0:
/tmp/buildcf0caf15e3c474ad628f15d324d6ff15.tmp/sketch/AClass.h:6:7: error: forward declaration of 'class BClass'
class BClass;
^
exit status 1
forward declaration of 'class BClass'

The error message is as expected:

error: invalid use of incomplete type 'class BClass'

Due to the circular dependency:
BClass has refAClass.
AClass has ptrBClass.

Is there a way to compile a circular dependency in Arduino 1.6.7?

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.

PaulS,

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

AClass.cpp

#include "AClass.h"

void AClass::initialize(BClass* ptr) { ptrBClass = ptr; }
void AClass::printB() { ptrBClass->print(); }
void AClass::print() { Serial.print("a"); }

BClass.h

#ifndef BCLASS_H
#define BCLASS_H

#include <Arduino.h>

class AClass;

class BClass
{
    private:
        AClass& refAClass;
    public:
        BClass(AClass& refAClass);
        void printA();
        void print();
};
#endif

BClass.cpp

#include "BClass.h"

BClass::BClass(AClass& refAClass) : refAClass(refAClass) { }
void BClass::printA() { refAClass.print(); }
void BClass::print() { Serial.print("b"); }

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.

Thank you PaulS. The following circular dependency example works as intended.

main.ino:

#include "AClass.h"
#include "BClass.h"

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

#include "AClass.h"
#include "BClass.h" //included here for circular dependency

void AClass::initialize(BClass* ptr) { ptrBClass = ptr; }
void AClass::printB() { ptrBClass->print(); }
void AClass::print() { Serial.print("a"); }

AClass.cpp

#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

BClass.h

#include "BClass.h"
#include "AClass.h" //included here for circular dependency

BClass::BClass(AClass& refAClass) : refAClass(refAClass) { }
void BClass::printA() { refAClass.print(); }
void BClass::print() { Serial.print("b"); }

BClass.cpp

#ifndef BCLASS_H
#define BCLASS_H

#include <Arduino.h>

class AClass;

class BClass
{
    private:
        AClass& refAClass;
    public:
        BClass(AClass& refAClass);
        void printA();
        void print();
};
#endif

output:

b
a