How to make global variables accessible to libraries/classes?

Long story short, I have a rather lengthy and complex code that I initially wrote on Visual Studio for ease of debugging, but now I want to burn onto the Arduino's memory. There are are six classes (hence six libraries) but the problem is there are also a bunch of global matrices and arrays that are being accessed by all the classes. Now this wasn't much of a problem in VS because I have all the code written in a single source file, but since the Arduino IDE doesn't allow me to define classes in the sketch, how do I make these matrices/arrays accessible to all the libraries as well as the main sketch?

since the Arduino IDE doesn't allow me to define classes in the sketch

Now I am puzzled. What gave you this idea? The IDE doesn't enforce syntax, and the compiler is a C++ compiler.

It would help to understand the current organization of the code a little better. Where are the globals declared in your big pile of code? And where are they defined?

Can you post the code, piecemeal if needed, in code tags, please?

-br

Now I am puzzled. What gave you this idea? The IDE doesn't enforce syntax, and the compiler is a C++ compiler.

The IDE blunders with a few things still, classes not working can be a side-effect. Use of user defined types usually indicates some use of references, if this is the case with ali250, then there is a solution at hand.

this code for example won't compile:

struct foo{
  int a;
  char b;
};

void setup(){}
void loop(){}

void foofunc( foo &f )
  {
    f.a = random( 0, 0xffff );
  }

The fix for this case is to add the function prototype below the struct.

void foofunc( foo &f );

If this is not the problem, you can still use classes in other files, and use the 'extern' keyword on variables you want to be seen across different files.

Globals suck though, even more as you want classes to access them. The variables are probably better off as static members of the classes or in interfaces themselves ( static members of their own class, say "configuration" class )

Basically I'm working on a checkers game, one where a person can play against a computer opponent on an actual checkers board. I'm using LEDs to model game pieces (to eliminate motorized movement of pieces and other complications like that). This was my initial plan of action:

  1. Make the entire game system and AI on Visual Studio (since it's easy to debug).
  2. Separately, on the Arduino IDE, make a class that will basically take data from the AI/game system and translate it onto the checkers board or alternatively, sense inputs from the checkers board and forward it to the AI for processing. This class obviously couldn't be made on VS since it involves Arduino-specific functions and libraries.
  3. Once both are finalized, merge the two codes.

Step 1 is complete. Step 2 is where I ran into problems. Initially I made all the functions required for interacting with the checkers board to see if they actually worked. Once they did, I basically just made the very same functions members of a class and tried to run that code - it would compile okay, but wouldn't run AT ALL. I mean I used Serial.print in the main loop, in the class functions, and in the constructor, but my serial monitor was completely blank. I googled the problem, and came across this:

the first answer says "You also need to make new files for your classes, you can't just declare them in your main sketch."

As far as global variables are concerned, there is a two dimensional (8x8) int array to simulate the checkers board, and two 1D arrays of human and computer players. As you may have guessed, the entire program involves analyzing and manipulating the data in these arrays so I needed to make them global. Also, one thing I forgot to mention in my initial post: the classes themselves are linked to each other as well, some are friends, and there's aggregation in a few of them as well. I basically want to know how I can get all this code working on the Arduino IDE like it does in VS.

You can use this method, it highlights multiple classes that use each other and globally accessible variables:

Globals.h

#ifndef HEADER_GLOBALS
  #define HEADER_GLOBALS
  extern char grid[ 8 ][ 8 ];
#endif

A.h

#ifndef HEADER_A
  #define HEADER_A

  #include "Globals.h"
  #include "B.h"

  class A{
    public:
      A();
    protected:
      friend class B;
  };
#endif

A.cpp

#include "A.h"

A::A(){
  return;
}

B.h

#ifndef HEADER_B
  #define HEADER_B

  #include "Globals.h"

  class B{
    public:
      B();
  };
#endif

B.cpp

#include "B.h"

B::B(){
  return;
}

Sketch:

#include "A.h"

char grid[ 8 ][ 8 ];

void setup(){}
void loop(){}

The sketch needs only include “A.h” due to A.h including B.h and Globals.h.

I mean I used Serial.print in the main loop, in the class functions, and in the constructor…

It’s my understanding that your constructor can’t do much because the Arduino hardware isn’t initialized yet. Printing is a no-no, millis() isn’t initialized, and so on. Perhaps someone more familiar with the issue can jump in with more if I’ve got this wrong.

You could use a .begin() method, like Serial does, to work around this.

-br

The fact that serial.print didn't work in loop suggests that you have a serious issue or no serial.begin in setup perhaps. I'd suggest that you back out code from your sketch until that at least works and then add your functionality back gradually.

As to sharing globals, use a begin method as billroy suggests and pass a pointer to your arrays and any other data there.

I don’t get it. The functions I made worked perfectly when I used them normally without classes. What I did to implement them in a class was:

  1. Shift all the statements (like Serial.begin, SPI.begin etc) that were in setup() to the class’s constructor.
  2. Create a global object before loop().

Doesn’t this effectively just work like setup()? As in when the global object gets created (once), the constructor gets called and basically functions like setup()? Sorry if I’m doing something really stupid here. I’m pretty new to Arduino and took on a pretty challenging project considering my skill level :confused:

pYro_65:
You can use this method, it highlights multiple classes that use each other and globally accessible variables:

Globals.h

#ifndef HEADER_GLOBALS

#define HEADER_GLOBALS
 extern char grid[ 8 ][ 8 ];
#endif




A.h


#ifndef HEADER_A
 #define HEADER_A

#include “Globals.h”
 #include “B.h”

class A{
   public:
     A();
   protected:
     friend class B;
 };
#endif




A.cpp


#include “A.h”

A::A(){
 return;
}




B.h


#ifndef HEADER_B
 #define HEADER_B

#include “Globals.h”

class B{
   public:
     B();
 };
#endif




B.cpp


#include “B.h”

B::B(){
 return;
}




Sketch:


#include “A.h”

char grid[ 8 ][ 8 ];

void setup(){}
void loop(){}




The sketch needs only include "A.h" due to A.h including B.h and Globals.h.

Thanks. I’ll try it out. But will this allow for friend classes and aggregation?

While the use of extern is alright in some cases, you risk problems with maintainability of your code, especially if you ever want to share it with someone. The better (correct :wink: ) way to do it is to use a pointer and use initialisation functions to share the variable, i.e.:

//in library file:
int* p_x;

void initialiseX(int* x){
  p_x=x;
}

void someOtherFunc(){
   Serial.print(*p_x);
}

//in main code file:
int x=5;

initialise(&x);
  1. Shift all the statements (like Serial.begin, SPI.begin etc) that were in setup() to the class's constructor.

That's where it all went pear shaped. There's no means to ensure that the initialization of the arduino is complete when your constructor is invoked. Until it is, calling things that depend on that setting of the hardware, such as serial.begin, is a dubious practice. This is why, as billroy suggested, so many arduino library classes have a begin method that is expected to be called in setup after all the initialization is complete.

Strange. When I wrote the class and it didn't work, I had a hunch the constructor wasn't functioning as a setup(), so I explicitally called the constructor in the setup too, but that didn't solve the problem so I thought something else was wrong. But now, I just put all the setup statements in another class function and called that in setup(). Program works fine now. Thanks.

My final query: will the method proposed by pyro_65 allow friend classes, friend functions and aggregation?

There is nothing in the IDE that stops you using normal C++ classes, with the possible exception of exceptions which are disabled with a compiler switch.

You can put classes in the main sketch. Everything else relevant, like friend functions will work, of course. This is Gnu C++ not some home-grown compiler.

I had a hunch the constructor wasn't functioning as a setup(), so I explicitally called the constructor in the setup too, but that didn't solve the problem so I thought something else was wrong

I don't see how you can "put the constructor in setup". The constructor is called when the instance is created, and unless you use dynamic memory allocation (ie. "new") then the class instance will go out of scope when you leave setup.

  1. Shift all the statements (like Serial.begin, SPI.begin etc) that were in setup() to the class's constructor.

As others have advised, don't do major things in the constructor. Initialize some integer variables, maybe (like baud rate). Then make a begin() class member and call that in setup(). This is what virtually every other class on the Arduino does, simply because you cannot guarantee what order constructors are called in, and the order (if you knew it) is probably not the one you want.

Google the "static initialization order fiasco". There are lots of hits explaining why initializing things in constructors in statically instantiated classes is a bad idea. Here is one of them.

http://www.parashift.com/c++-faq/static-init-order.html

This is nothing to do with the Arduino, BTW. It's a C++ thing.