Problem with C++ vectors compiling with the Arduino IDE

I'm trying to pass a reference to a vector object into a function and am having problems with it compiling. I have two simple classes. The first one compiles just fine as well as the function which takes a reference to a vector of the objects. But a second class, almost identical to the first, FAILS and reports a compile error, that the class is "not declared in this scope".

Here's the code:

#include <vector>

class SimpleClass
{
    unsigned int X;
    unsigned int Y;
};

std::vector<SimpleClass> VectorOfSimpleClassObjects; // Compiles OK

void SimpleXYZ(std::vector<SimpleClass> &TheVectorOfSimpleClassObjects) // Compiles OK
{
  
}

class AnotherSimpleClass
{
    unsigned int X;
    unsigned int Y;
};

std::vector<AnotherSimpleClass> VectorOfAnotherSimpleClassObjects; // Compiles OK

// But THIS generates the error: AnotherSimpleClass' was not declared in this scope
void AnotherSimpleXYZ(std::vector<AnotherSimpleClass> &TheVectorOfAnotherSimpleClassObjects)
{
  
}

What am I doing wrong? I've done a bunch of testing and it seems to accept a first set of code that compiles just fine but then a subsequent, almost identical, SECOND set of code in the same program results in a compile error.

I'm quite baffled.

Maybe the class needs a public null constructor?

class AnotherSimpleClass
{
    unsigned int X;
    unsigned int Y;
public:
    AnotherSimpleClass(){}
};

The Arduino IDE adds function prototypes at the top of your sketch. Sometimes it gets it wrong and moves the function declaration before the type definition it relies on.

Either move your second class to the top of your file, where the other class is defined, or add your own function prototypes.

Pieter

PieterP:
The Arduino IDE adds function prototypes at the top of your sketch. Sometimes it gets it wrong and moves the function declaration before the type definition it relies on.

Nice catch.

PieterP:
The Arduino IDE adds function prototypes at the top of your sketch. Sometimes it gets it wrong and moves the function declaration before the type definition it relies on.

I moved the second class definition so that it immediately follows the first one and now it compiles without error.

So, thanks, ... but ...

Yikes! That's just absurd. I've spent hours trying all sorts of different approaches to no avail. I'm a retired software engineer with 45 years experience with C and C++ and have never encountered anything like that.

I prefer keeping classes near where I first use them, to keep the code nice and clean. But now I have to go back to my very large, complex, program where I first encountered this problem and, I assume, move all my class definitions to the top of the program so it can avoid this weird behavior.

1 Like

RogerInHawaii:
Yikes! That's just absurd. I've spent hours trying all sorts of different approaches to no avail. I'm a retired software engineer with 45 years experience with C and C++ and have never encountered anything like that.

I prefer keeping classes near where I first use them, to keep the code nice and clean. But now I have to go back to my very large, complex, program where I first encountered this problem and, I assume, move all my class definitions to the top of the program so it can avoid this weird behavior.

These are the exact same requirements as working in any other C++ environment. Classes have to be defined before they are used and function prototypes are required if you try to call the function before it is defined. The only catch is that if you don't supply prototypes, the Arduino IDE will possibly do so in the wrong place. So, as long as you supply your own prototypes (which you should be doing anyway) and don't rely on the Arduino IDE supplying them, things work as usual. This compiles fine for an ESP8266:

#include "Arduino.h"
#include <vector>

class SimpleClass {
    unsigned int X;
    unsigned int Y;
};

class AnotherSimpleClass {
    unsigned int X;
    unsigned int Y;
};

void SimpleXYZ(std::vector<SimpleClass> &v);
void AnotherSimpleXYZ(std::vector<AnotherSimpleClass> &v);

void setup() {
  std::vector<SimpleClass> VectorOfSimpleClassObjects;
  std::vector<AnotherSimpleClass> VectorOfAnotherSimpleClassObjects;

  SimpleXYZ(VectorOfSimpleClassObjects);
  AnotherSimpleXYZ(VectorOfAnotherSimpleClassObjects);

}

void loop() {
}

void SimpleXYZ(std::vector<SimpleClass> &TheVectorOfSimpleClassObjects) {
}

void AnotherSimpleXYZ(std::vector<AnotherSimpleClass> &TheVectorOfAnotherSimpleClassObjects) {
}

There are also several Eclipsed-based IDEs that will work in the Arduino ecosystem. You may want to check them out seeing as how you are an experienced programmer.

What's a ?

-jim lee

This phenomenon of the unwanted, hidden and, maybe, badly formed function prototypes, created before the data types of any arguments, which it may use, have been declared, happens in .ino files but not .cpp files.

It seems, however, to have improved in the later Arduino IDE releases.

The version of Sloeber Arduino Eclipse plug in (4.3) I use does the same thing but, at least there, it is transparent. You can see how the Arduino build routine has placed the function prototypes it has created and edit them out although, annoyingly, they have a habit of reappearing.

I guess the original idea was to ‘protect’ new users from the principle that a function has to be declared before it is used. However, it has not been well executed and causes problems, especially for experienced users, who are likely to be using constructs that the build routine does not understand.

RogerInHawaii:
Yikes! That's just absurd. I've spent hours trying all sorts of different approaches to no avail. I'm a retired software engineer with 45 years experience with C and C++ and have never encountered anything like that.

Yeah - the stuff they do to make it convenient for newbies can tend to be frustrating when it comes to more complex programs. Arduino is a platform for hobbyists and people new to microcontrollers and programming.

The solution is to put nothing at all in the .ino file, and to move loop() and setup() into a .cpp file on a new tab (don't forget to #include <Arduino.h>). You would then forward-declare things in the usual cpp way.

jimLee:
What's a ?

-jim lee

jimLee:
What's a ?

-jim lee

A vector is somewhat like an array, except that you don't need to declare, either explicitly or implicitly, how many items are in it. You can add and delete items during the running of your program, implicitly expanding or reducing the overall size of it.

Just do a search on "C++ vectors" and you'll get lots of info about them.

They can be really useful.

Are you using this? If not, give the correct link of the library...By the way, you gotta use a library...!

Cause I feel you ain't using a library.. #include looks like a line straight from C++!

TheUNOGuy:
...By the way, you gotta use a library...!

As shown in my Reply #5, that's untrue ---- at least for some processors. For ESPs (and perhaps others) the STL is included as part of the standard core for the board.

RogerInHawaii:
A vector is somewhat like an array, except that you don't need to declare, either explicitly or implicitly, how many items are in it. You can add and delete items during the running of your program, implicitly expanding or reducing the overall size of it.

Just do a search on "C++ vectors" and you'll get lots of info about them.

They can be really useful.

Oh, kinda' like a linked list with an index added? Got it.

Thanks!

-jim lee

No, not at all like a linked list.
A vector is a dynamic array. Its elements are laid out in one contiguous block of memory, exactly like an array, so it has constant O(1) random access time.
The difference between a normal array and a vector is that an array is fixed-size and allocated on the stack, while a vector can be resized and is dynamically allocated.

A linked-list is completely different. Its "nodes" can be scattered throughout memory, with an extra pointer in each node to point to the next one. It only has linear access, random access requires O(n) indirections because you have to walk through the list and follow all pointers to get to the n-th element.

Yeah, stretchy array.

-jim lee

Huh.

Does that mean that you can't do "&myvector[j]" if myvector is a vector?

westfw:
Does that mean that you can't do "&myvector[j]" if myvector is a vector?

Never used std::vector<>. But in the source code (for ESP8286 anyway), operator[] returns a reference to the element at the specified index. So, presumably, you can then take its address.

Note that operator[] does not check the validity of the index supplied (same as a regular C / C++ array). Looks like the at() method provides checking.

Also, again having not used vectors, can they be moved in memory if necessary for resizing? If so, that pointer you got with the & operator will then be pointer to the wrong place. I think the whole point of these STL contains is to provide safe access methods so you don't need to do stuff with raw pointers.

westfw:
Huh.

Does that mean that you can't do "&myvector[j]" if myvector is a vector?

Taking the address of an element is fine: std::vector - cppreference.com

The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements. This means that a pointer to an element of a vector may be passed to any function that expects a pointer to an element of an array.

You do have to be careful when performing operations that might cause reallocation of the underlying storage, because that could invalidate all pointers and iterators.
The previous link has a section "Iterator invalidation" that covers these operations.

OK, I took the advice of placing ALL class definitions at the very beginning of my fairly large application program and now it compiles without error.

But this is just a very ridiculous requirement.

The Arduino IDE compiler is apparently trying to make it easier for very beginner programmers and hobbyists to write programs using it by playing some tricks during the compile, in particular by "behind the scenes" moving some things around in its temporary, internal, representation of the source code (though not the source code itself) in hopes of avoiding errors caused by names being referenced before they're defined.

In my particular case I was defining the name of a vector type and then using that in the parameter list of a function definition. The function immediately followed the definition of the vector type, a perfectly valid sequence in C++, perfectly valid good programming practice. But the Arduino IDE, in its attempt to make things "easier" for beginning programmers, did something internally that resulted in it reporting an error in my code (which wasn't actually there), indicating that the vector type was not defined, when in fact it was.

The fact is, C++ is an advanced programming language. If that's the programming language that you're providing, via the IDE, then you should abide by the fact that it's an advanced programming language and requires some skill in using it. The approach of trying to accommodate inexperienced programmers by automatically rearranging things is just a very bad approach, especially when it negatively impacts (makes a program un-compilable) for experienced programmers abiding by valid, established, programming practices.

The better approach is for the Arduino IDE is for it to compile just like any other C++ compiler, NOT trying to accommodate beginners and hobbyists by playing games with the code when it compiles. If a programmer fails to define some name in the source code prior to its use then the compiler should report that as an error, perhaps,

"The word, "XYZ", is not defined in the current context. It must be defined in the source code PRIOR to its use."

And if the compiler wants to be even more helpful it might detect that it indeed IS defined further down in the code and advise the programmer that he might consider moving either the definition or its use so that the definition comes before the use.

THAT would be helpful to the programmer, beginner or not, but especially for the beginner because it helps to educate him on the proper design of a program.