Different Classes Can't Access Each Other?

What I want here is for different classes (declared in the same scope) to be able to access each other's public methods and properties. I have spent several days already looking around online and trying to find any way to do this, and I have not found any way that works.

I have found at least a couple ways of passing a class by reference (putting & after the class type, and putting & before the instance name in the function prototype) but neither of those makes any difference. One attempt at this allowed me to access the first-declared-property of the target class, but no other properties or methods were usable remotely.***

A lot of the examples I found online will compile without errors, but they simply don't work. I could set an initial property by the class that it was in, and then try to serial.print it from another class, and the result is completely incorrect--but is consistently incorrect--leading me to believe that the referencing is getting mangled somewhere along the way. (Also for the reason *** noted above)

There are lots of posts all around that claim there is a way to do this just by passing a reference, but I can not get any of their examples to work, or their examples are a highly specialized particular situation that wasn't helpful to what I want to do.

This example on stackexchange:

Is specific to Arduino, is relatively recent and says to create another class that includes references for all the other instances you have, and then put a reference to that into all the other classes. I tried that and could not get it to work either. But if the reference-passing doesn't work right, that isn't real surprising.

The exact problem is that in the Arduino IDE, any one object does not recognize any other class that isn't passed into it, even ones declared in the same scope. I am not a professional programmer but I have some education and some years experience with coding in a few languages, and have not run across this problem with other platforms and languages.

I could just copy an external object to an internal one in a different class, but it seems like there should be some way to do this by reference???

There are a few posts (some from here) from ~2010 and earlier, mentioning that the preprocessor that the Arduino IDE compiler uses is set up in an unconventional way, that happens to break this particular functionality. Is this still true? There are a LOT of posts asking about this exact problem, and many of them are about the Arduino IDE....

Here is a simple example sketch, that doesn't work:

Is it possible to do this by passing values? Or is pointers the only option?

Post your code using code tags here.

Not going to bother with pastebin.com or whatever!

It can't work, in "Thing2" you are (with a wrong syntax) statically referencing fields from "Thing1" (Thing1.Value[1|2|3]). You are referencing types, not variables. For your code to work, you must do something like this (not testet, though):

#include "Arduino.h"

//Forward declarations (meaning that these will be declared later than they are available for the code).
class Thing1;
class Thing2;

//Global variables
Thing1 example1;
Thing2 example2;

//Implementation of Thing1 & Thing2
class Thing1
{
  public:
    Thing1();
    int Value1;
    float Value2;
    long Value3;
};
 
Thing1::Thing1() {
  Value1 = 0;
  Value2 = 0;
  Value3 = 0;
}

class Thing2
{
  public:
    Thing2();
    void Change_Thing1_Value1(int);
    void Change_Thing1_Value2(float);
    void Change_Thing1_Value3(long);
};
 
Thing2::Thing2() {
  // nuthin
}
 
void Thing2::Change_Thing1_Value1(int cValue)
{
  example1.Value1 = cValue; //Referencing valid global variable
}
 
void Thing2::Change_Thing1_Value2(float cValue)
{
  example1.Value2 = cValue; //Referencing valid global variable
}
 
void Thing2::Change_Thing1_Value3(long cValue)
{
  example1.Value3 = cValue; //Referencing valid global variable
}
 
void setup() {
  Serial.begin(9600);
  example1.Value1 = 3; // Set initial values here.
  example2.Value2 = 4.3;
  example2.Value3 = 8;
}
 
void loop() {
 
  // Print the initial values
  Serial.println("Point #1");
  Serial.println(example1.Value1);
  Serial.println(example1.Value2);
  Serial.println(example1.Value3);
  Serial.println(" ");
 
  // Change the initial values
  example2.Change_Thing1_Value1(4);
  example2.Change_Thing1_Value2(5.1);
  example2.Change_Thing1_Value3(11);
 
  // Print the new values
  Serial.println("Point #2");
  Serial.println(example1.Value1);
  Serial.println(example1.Value2);
  Serial.println(example1.Value3);
  Serial.println(" ");
 
  Serial.println("Finished");
 
  delay(10000);
}

However, this code makes absolutely no sense..

Danois90:
It can't work, in "Thing2" you are (with a wrong syntax) statically referencing fields from "Thing1" (Thing1.Value[1|2|3]). You are referencing types, not variables. For your code to work, you must do something like this (not testet, though): ...

That doesn't work.
The specific error is (in part)

ClassTester_UsePublic01_answer01:8: error: aggregate 'Thing1 example1' has incomplete type and cannot be defined

,,,

ClassTester_UsePublic01_answer01:9: error: aggregate 'Thing2 example2' has incomplete type and cannot be defined

,,,

To do this at all, it appears that all of any classes that could access each other would need to be somehow defined at the same time. Or one could put all the ~10 different classes for different parts of the program, into one container class. But that is what the one guy on the linked StackExchange post said not to do??? He implies that there is another way, but he does not explain further.

However, this code makes absolutely no sense..

The point was to have different classes for different types if hardware for a machine, and be able to send data from an input class straight to an output class. Rather than have the input class set values in some plain global variables, and then have the output class periodically check them for data that it can act upon.

I cannot find any working example specific to the Arduino IDE, so it may not be possible under this circumstance.

As requested in Reply #1, post your (original, non-working) code here -- in CODE TAGS.

Few people are interested in going to other web pages to get your code.

Make it easy for people who might be willing to help.

I would generally presume Pastebin (or somewhere similar) to be a better choice than posting code here, since Pastebin (and other places) display visible line numbers that make discussing code much easier.

/*
   April 27, 2018
   This sketch doesn't work.
   It is an example of an attempt to allow one class to access the public properties of another different class.
*/
 
#include "Arduino.h"
 
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class Thing1
{
  public:
    Thing1();
    int Value1;
    float Value2;
    long Value3;
};
 
Thing1::Thing1() {
  Value1 = 0;
  Value2 = 0;
  Value3 = 0;
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class Thing2
{
  public:
    Thing2();
    void Change_Thing1_Value1(int);
    void Change_Thing1_Value2(float);
    void Change_Thing1_Value3(long);
};
 
Thing2::Thing2() {
  // nuthin
}
 
void Thing2::Change_Thing1_Value1(int cValue)
{
  Thing1.Value1 = cValue; // This is what should happen here.
}
 
void Thing2::Change_Thing1_Value2(float cValue)
{
  Thing1.Value2 = cValue; // This is what should happen here.
}
 
void Thing2::Change_Thing1_Value3(long cValue)
{
  Thing1.Value3 = cValue; // This is what should happen here.
}
 
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
Thing1 example1 = Thing1();
Thing2 example2 = Thing2();
 
void setup() {
  Serial.begin(9600);
  example1.Value1 = 3; // Set initial values here.
  example2.Value2 = 4.3;
  example2.Value3 = 8;
}
 
void loop() {
 
  // Print the initial values
  Serial.println("Point #1");
  Serial.println(example1.Value1);
  Serial.println(example1.Value2);
  Serial.println(example1.Value3);
  Serial.println(" ");
 
  // Change the initial values
  example2.Change_Thing1_Value1(4);
  example2.Change_Thing1_Value2(5.1);
  example2.Change_Thing1_Value3(11);
 
  // Print the new values
  Serial.println("Point #2");
  Serial.println(example1.Value1);
  Serial.println(example1.Value2);
  Serial.println(example1.Value3);
  Serial.println(" ");
 
  Serial.println("Finished");
 
  delay(10000);
}
Thing1.Value1 = cValue; // This is what should happen here.

That would change the value of the OBJECT Thing1. But there is non, there is only a class Thing1. Which is only a blueprint for making the object. Aka, there is no value 'Thing1.Value1'

Btw, making variables public isn't a good design most of the time :wink:

Doug384:
I would generally presume Pastebin (or somewhere similar) to be a better choice than posting code here.

Nope. And, the "Sticky" posts at the top of every forum page entitled:
"How to use this forum - please read."
"Read this before posting a programming question ..."

Explain how to post code. That's why they are there, just waiting to for newbies to read them.

Anyway, this compiles and works:

/*
   April 27, 2018
   This sketch doesn't work.
   It is an example of an attempt to allow one class to access the public properties of another different class.
*/
 
#include "Arduino.h"
 
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class Thing1
{
  public:
    Thing1();
    int Value1;
    float Value2;
    long Value3;
};
 
Thing1::Thing1() {
  Value1 = 0;
  Value2 = 0;
  Value3 = 0;
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
class Thing2
{
  public:
    Thing2();
    void Change_Thing1_Value1(Thing1 &, int);
    void Change_Thing1_Value2(Thing1 &, float);
    void Change_Thing1_Value3(Thing1 &, long);
};
 
Thing2::Thing2() {
  // nuthin
}
 
void Thing2::Change_Thing1_Value1(Thing1 &thing1Instance, int cValue)
{
  thing1Instance.Value1 = cValue; // This is what should happen here.
}
 
void Thing2::Change_Thing1_Value2(Thing1 &thing1Instance, float cValue)
{
  thing1Instance.Value2 = cValue; // This is what should happen here.
}
 
void Thing2::Change_Thing1_Value3(Thing1 &thing1Instance, long cValue)
{
  thing1Instance.Value3 = cValue; // This is what should happen here.
}
 
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
Thing1 example1;
Thing2 example2;
 
void setup() {
  Serial.begin(115200);
  delay(1000);

  example1.Value1 = 3; // Set initial values here.
  example1.Value2 = 4.3;
  example1.Value3 = 8;

    // Print the initial values
  Serial.println("Point #1");
  Serial.println(example1.Value1);
  Serial.println(example1.Value2);
  Serial.println(example1.Value3);
  Serial.println(" ");
 
  // Change the initial values
  example2.Change_Thing1_Value1(example1, 4);
  example2.Change_Thing1_Value2(example1, 5.1);
  example2.Change_Thing1_Value3(example1, 11);
 
  // Print the new values
  Serial.println("Point #2");
  Serial.println(example1.Value1);
  Serial.println(example1.Value2);
  Serial.println(example1.Value3);
  Serial.println(" ");
 
  Serial.println("Finished");
}
 
void loop() {}

gfvalvo:
Anyway, this compiles and works:

but:

void Thing2::Change_Thing1_Value1(Thing1 &thing1Instance, int cValue)
{
  thing1Instance.Value1 = cValue; // This is what should happen here.
}

It seems to me that this circular dependency is not really what you want.

you should be passing references to each (interdependent) class, IMHO.

BulldogLowell:
you should be passing references to each (interdependent) class, IMHO.

Example?

gfvalvo:
Example?

well this is a wonky example, but it shows my point:

class ClassB;
class ClassA;

class ClassB {
  public:
    ClassB() {};
    void worldHello(const ClassA& a);
  private:
    const char* _bText = "World";
    void printB();
  friend class ClassA;
};

class ClassA {
  public:
    ClassA() {};
    void helloWorld(const ClassB& b);
  private:
    const char* _aText = "Hello";
    void printA();
  friend class ClassB;
};

void ClassA::helloWorld(const ClassB& b) {
  Serial.print(_aText);
  Serial.print(" ");
  b.printB();
};

void ClassA::printA() {
  Serial.println(_aText);;
};

void ClassB::worldHello(const ClassA& a) {
  Serial.print(_bText);
  Serial.print(" ");
  a.printA();
};

void ClassB::printB() {
  Serial.println(_bText);;
};

ClassA instanceOfA;
ClassB instanceOfB;
ClassB someInstance;  // portability

void setup() {
  Serial.begin(9600);
  instanceOfA.helloWorld(instanceOfB);
  instanceOfB.worldHello(instanceOfA);
  someInstance.worldHello(instanceOfA);
}

void loop() {
}

Compiles with warnings:

C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_208769\sketch_apr28a.ino: In member function 'void ClassA::helloWorld(const ClassB&)':

C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_208769\sketch_apr28a.ino:27:12: warning: passing 'const ClassB' as 'this' argument of 'void ClassB::printB()' discards qualifiers [-fpermissive]

   b.printB();

            ^

C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_208769\sketch_apr28a.ino: In member function 'void ClassB::worldHello(const ClassA&)':

C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_208769\sketch_apr28a.ino:37:12: warning: passing 'const ClassA' as 'this' argument of 'void ClassA::printA()' discards qualifiers [-fpermissive]

   a.printA();

            ^

Sketch uses 1610 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 208 bytes (10%) of dynamic memory, leaving 1840 bytes for local variables. Maximum is 2048 bytes.

Those warnings are called errors if you try to compile for Teensy:

Arduino: 1.8.5 (Windows 7), TD: 1.40, Board: "Teensy 3.2 / 3.1, Serial, 96 MHz (overclock), Faster, US English"

Build options changed, rebuilding all
C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_694491\sketch_apr28a.ino: In member function 'void ClassA::helloWorld(const ClassB&)':

sketch_apr28a:27: error: passing 'const ClassB' as 'this' argument discards qualifiers [-fpermissive]
   b.printB();

            ^

C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_694491\sketch_apr28a.ino:10:10: note:   in call to 'void ClassB::printB()'

     void printB();

          ^

C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_694491\sketch_apr28a.ino: In member function 'void ClassB::worldHello(const ClassA&)':

sketch_apr28a:37: error: passing 'const ClassA' as 'this' argument discards qualifiers [-fpermissive]
   a.printA();

            ^

C:\Users\GFV\AppData\Local\Temp\arduino_modified_sketch_694491\sketch_apr28a.ino:30:6: note:   in call to 'void ClassA::printA()'

 void ClassA::printA() {

      ^

passing 'const ClassB' as 'this' argument discards qualifiers [-fpermissive]

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Compiler is happier without the 'const' specifiers. No warnings, no errors:

class ClassB;
class ClassA;

class ClassB {
  public:
    ClassB() {};
    void worldHello(ClassA& a);
  private:
    const char* _bText = "World";
    void printB();
  friend class ClassA;
};

class ClassA {
  public:
    ClassA() {};
    void helloWorld(ClassB& b);
  private:
    const char* _aText = "Hello";
    void printA();
  friend class ClassB;
};

void ClassA::helloWorld(ClassB& b) {
  Serial.print(_aText);
  Serial.print(" ");
  b.printB();
};

void ClassA::printA() {
  Serial.println(_aText);;
};

void ClassB::worldHello(ClassA& a) {
  Serial.print(_bText);
  Serial.print(" ");
  a.printA();
};

void ClassB::printB() {
  Serial.println(_bText);;
};

ClassA instanceOfA;
ClassB instanceOfB;
ClassB someInstance;  // portability

void setup() {
  Serial.begin(115200);
  delay(1000);
  instanceOfA.helloWorld(instanceOfB);
  instanceOfB.worldHello(instanceOfA);
  someInstance.worldHello(instanceOfA);
}

void loop() {
}

The only way around the incomplete class error is to use pointers:

#include "Arduino.h"

//Forward declarations (meaning that these will be declared later than they are available for the code).
class Thing1;
class Thing2;

//Global variables
Thing1 *example1;
Thing2 *example2;

//Implementation of Thing1 & Thing2
class Thing1
{
  public:
    Thing1();
    int Value1;
    float Value2;
    long Value3;
};
 
Thing1::Thing1() {
  Value1 = 0;
  Value2 = 0;
  Value3 = 0;
}

class Thing2
{
  public:
    Thing2();
    void Change_Thing1_Value1(int);
    void Change_Thing1_Value2(float);
    void Change_Thing1_Value3(long);
};
 
Thing2::Thing2() {
  // nuthin
}
 
void Thing2::Change_Thing1_Value1(int cValue)
{
  example1->Value1 = cValue; //Referencing valid global variable
}
 
void Thing2::Change_Thing1_Value2(float cValue)
{
  example1->Value2 = cValue; //Referencing valid global variable
}
 
void Thing2::Change_Thing1_Value3(long cValue)
{
  example1->Value3 = cValue; //Referencing valid global variable
}
 
void setup() {
  Serial.begin(9600);
  example1 = new Thing1();
  example2 = new Thing2();
  example1->Value1 = 3; // Set initial values here.
  example1->Value2 = 4.3;
  example1->Value3 = 8;
}
 
void loop() {
 
  // Print the initial values
  Serial.println("Point #1");
  Serial.println(example1->Value1);
  Serial.println(example1->Value2);
  Serial.println(example1->Value3);
  Serial.println(" ");
 
  // Change the initial values
  example2->Change_Thing1_Value1(4);
  example2->Change_Thing1_Value2(5.1);
  example2->Change_Thing1_Value3(11);
 
  // Print the new values
  Serial.println("Point #2");
  Serial.println(example1->Value1);
  Serial.println(example1->Value2);
  Serial.println(example1->Value3);
  Serial.println(" ");
 
  Serial.println("Finished");
 
  delay(10000);
}

Note that "." has become "->" and there is contruction of classes in setup().

But again, the code is junk and it makes no sense what anyone would try to achieve with this.