#define ArraySize 10
#include "TestDefine.h"
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
If I compile my ino-file, I get the below error:
In file included from C:\Users\hansb\Documents\Arduino\libraries\TestDefine\TestDefine.cpp:2:0:
C:\Users\hansb\Documents\Arduino\libraries\TestDefine\TestDefine.h:12:16: error: 'ArraySize' was not declared in this scope
int _array[ArraySize];
^~~~~~~~~
I don't get this! The #define statement in my ino-file comes BEFORE I include the TestDefine.h. So why do I get this error?
If that doesn't work, is there another way to define my ArraySize in my ino-file when using my library? I need that flexibility, because my library is being re-used in several ino-files, but each time with it's own ArraySize.
"TestDefine.h" does not include "test.ino", so the define is not available in the header.
EDIT: A sollution could be a template class:
//HEADER
template<uint16_t ArraySize> class Test
{
public:
Test(int pin);
private:
int _array[ArraySize];
};
//SKETCH
Test<10> myTest(); //Array of 10 elements
Test<100> myTest(); //Array of 100 elements
Just pass a value into the constructor. Allocate it in RAM. Then make sure you deallocate it in your destructor. You can even set up a default value, so if its not a big deal to the user it still works.
I don't get this! The #define statement in my ino-file comes BEFORE I include the TestDefine.h. So why do I get this error?
Arduino IDE first compiles all the libraries separately and then compiles your .ino file. So your library does not have any knowledge of the defines in your .ino. Even if you put the #define before the #include.
Amazing how fast I'm getting answers on this forum
@Danois90
I will definitely try your suggestion (I could have done it first, but I had already written my below answer before I realized that you edited your reply).
@jimlee
I'm still a newbee, so might need some help here. I assume you mean doing the following?
determine how much memory my array will require (which in my opinion is as simple as multiplying ArraySize with SizeOf(int)?)
allocate the memory in the constructor - deallocate in the destructor
Bottom line is that I don't make use of an array anymore, but just use and integer pointer to access the integers? @pourduino
So what you mean is just adding a "settings.h" file in the library (means that the library contains 2 header files) in which I put the #include "TestDefine.h". I then include this header file in both my library and my sketch, right?
Bottom line: If Danois90's solution works, then this feels like the most elegant one. I will let you know the progress later this week.
jimLee: @Danois90 I liked you settings file idea. Why'd you pull it?
-jim lee
I havent pulled anything and I did not write the suggestion with a settings file. A settings file must be modified every time a sketch needs a different array size, having two different array sizes in one sketch is a no go, so that's not ideal. The template method is the only way to use static allocation within the class, but you could also initialize the array outside the class and pass it as an argument:
//HEADER
class Test
{
public:
Test(int pin, int* array, int array_size); //{ _array = array; _array_size = array_size};
private:
int _array_size;
int* _array;
};
//SKETCH
const int ARRAY_SIZE = 10;
int array[ARRAY_SIZE];
Test myTest(6, array, ARRAY_SIZE);
hansbilliet: @pourduino
So what you mean is just adding a "settings.h" file in the library (means that the library contains 2 header files) in which I put the #include "TestDefine.h". I then include this header file in both my library and my sketch, right?
jimLee: @Danois90 I liked you settings file idea. Why'd you pull it?
-jim lee
Sorry I didn't read the second part of the question. OP needs to dynamically change array size in each sketch, with settings file it will be globally modified for all sketches using that library.
So, three different techniques were suggested. All have their pros and cons:
Use a Template class: Pros: (1) The array size is set at compile-time so you'll know if you have sufficient memory for the particular processor. (2) The array can be completely controlled by the class (i.e.private).
Cons: (1) The entire class definition must be in the .h file, can't use a .cpp file.
Allocate the array in the .ino file and pass the class a pointer (or reference) and the array's size: Pros: (1) The array size is set at compile-time so you'll know if you have sufficient memory for the particular processor. (2) The class definition can be split into .h and .cpp files.
Cons: (1) The array IS NOT completely controlled by the class.
Use dynamic allocation: Pros: (1) The array can be completely controlled by the class (i.e.private). (2) The class definition can be split into .h and .cpp files.
Cons: (1) The array size IS NOT set at compile-time so you DON'T KNOW until run-time if you have sufficient memory for the particular processor. Your code must handle the case when malloc() / new returns a nullptr.
They all will work; pick the one you're comfortable with.
Thanks to you all! Great to have not 1, but even 3 possible solutions!!
Just some additional question.
Cons: (1) The entire class definition must be in the .h file, can't use a .cpp file.
Why is that exactly a con? I know that it is more common to have header files and cpp files separate. But are there real disadvantages of having the entire class definition in the header file?
hansbilliet:
Why is that exactly a con? I know that it is more common to have header files and cpp files separate. But are there real disadvantages of having the entire class definition in the header file?
Longer compile times, primarily. Since a header file could be included in multiple implementation files, the header is compiled multiple times. IIRC, each translation unit that includes the header will also end up with a weak version of the inline functions in that header file, which also slows down the compiler and the linker.
If you're just including it in your main .ino file and a handful of .cpp files, it's not going to be an issue at all.
Another issue that might arise when using templates is that the size of your program increases, since each combination of template parameters results in a separate class with separate code.
I'm a heavy template user, and I've never found this to be a problem in practice, even on an Arduino Uno with 32KiB of program space (and when I run out of memory, I usually run out of RAM before running out of program memory).
IMO, it's also a readability issue. With a nice short and clean .h file defining the class's interface, you can see at a glance the available public data members and signatures for public functions. You might not even need to look in the implementation (.cpp) to get the information you need.
Also, using templates prevents you from being able to distribute the class as a pre-compiled object file. This is not really a factor in the Arduino open-source ecosystem.
gfvalvo:
IMO, it's also a readability issue. With a nice short and clean .h file defining the class's interface, you can see at a glance the available public data members and signatures for public functions. You might not even need to look in the implementation (.cpp) to get the information you need.
You can put the implementation into a separate file (common convention is to use the ".tpp" file extension for templates), then include that file at the bottom of the header file:
gfvalvo:
IMO, it's also a readability issue. With a nice short and clean .h file defining the class's interface, you can see at a glance the available public data members and signatures for public functions. You might not even need to look in the implementation (.cpp) to get the information you need.
I often split up my template classes into a header file and a .ipp file (inline C++):
PieterP:
I often split up my template classes into a header file and a .ipp file (inline C++):
Arduino IDE v1.8.12 doesn't like the .ipp extension. So, I used MyClass.cpp instead and then tried to compile for an Uno. Resultant error messages:
Arduino: 1.8.12 (Windows 10), TD: 1.51, Board: "Arduino Uno"
In file included from sketch\MyClass.hpp:12:0,
from sketch\MyClass.cpp:1:
MyClass.cpp:5:6: error: 'template<unsigned int N> class MyClass' used without template parameters
void MyClass::print() const {
^~~~~~~
MyClass.cpp:5:23: error: non-member function 'void print()' cannot have cv-qualifier
void MyClass::print() const {
^~~~~
sketch\MyClass.cpp: In function 'void print()':
MyClass.cpp:6:33: error: 'data' was not declared in this scope
for (const uint8_t &element : data) {
^~~~
In file included from sketch\MyClass.hpp:12:0,
from D:\arduino-1.8.5\portable\sketchbook\sketch_oct26a\sketch_oct26a.ino:1:
MyClass.cpp:5:6: error: 'template<unsigned int N> class MyClass' used without template parameters
void MyClass::print() const {
^~~~~~~
MyClass.cpp:5:23: error: non-member function 'void print()' cannot have cv-qualifier
void MyClass::print() const {
^~~~~
sketch\MyClass.cpp: In function 'void print()':
MyClass.cpp:6:33: error: 'data' was not declared in this scope
for (const uint8_t &element : data) {
^~~~
sketch\MyClass.cpp:6:33: note: suggested alternative: 'atan'
for (const uint8_t &element : data) {
^~~~
atan
sketch\MyClass.cpp:6:33: note: suggested alternative: 'atan'
for (const uint8_t &element : data) {
^~~~
atan
sketch\MyClass.cpp: At global scope:
MyClass.cpp:5:6: error: 'template<unsigned int N> class MyClass' used without template parameters
void MyClass::print() const {
^~~~~~~
MyClass.cpp:5:23: error: non-member function 'void print()' cannot have cv-qualifier
void MyClass::print() const {
^~~~~
MyClass.cpp:5:6: error: redefinition of 'template<unsigned int N> void print()'
void MyClass::print() const {
^~~~~~~
In file included from sketch\MyClass.hpp:12:0,
from sketch\MyClass.cpp:1:
sketch\MyClass.cpp:5:6: note: 'template<unsigned int N> void print()' previously declared here
void MyClass::print() const {
^~~~~~~
exit status 1
'template<unsigned int N> class MyClass' used without template parameters
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
You need to put "template" in front of every method implementation in the *.cpp file. Every change in the template definition must also be modified for each implemented medthod, and that's why the "double include" approach IMHO is stupid.
Danois90:
You need to put "template" in front of every method implementation in the *.cpp file. Every change in the template definition must also be modified for each implemented medthod, and that's why the "double include" approach IMHO is stupid.
It's there:
#include "MyClass.hpp" // not necessary for compilation, but keeps my IDE happy
#include <Arduino.h>
template <size_t N> // <-------------------------
void MyClass::print() const {
for (const uint8_t &element : data) {
Serial.println(element);
}
}
Arduino IDE v1.8.12 doesn't like the .ipp extension. So, I used MyClass.cpp instead and then tried to compile for an Uno.
You can't use a .cpp file, you need the template declaration included in the header, and not compiled in a different translation unit. You can use .ipp as part of a library, but not inside of a sketch folder, it seems. You could use something like "MyClass.implementation.hpp", for example, possibly with some include guards to print a readable error message when people try to include it directly.
The compilation error is my fault, I copied and pasted the class name without double checking. You need to specify the template parameter when you use the class name outside of the class:
Every change in the template definition must also be modified for each implemented medthod, and that's why the "double include" approach IMHO is stupid.
The template arguments rarely ever change, especially if they're part of your public API, and even if you do need to change them for some reason, it takes 10 seconds using "search and replace" in your IDE.
I know I can Google it, but to make this thread complete...
I see now 5 possible file extensions. Can somebody explain shortly what each of them is? I gave my best guesses already, but are the extensions only a matter of "agreement", but can you use whatever extension you want (not that I intend to do so - I like agreements :)).
*.hpp (is this another extention used for header file?)
*.tpp (template implementation file ?)
*.ipp (inline code implementation file ?)
And finally some offtopic question. I see that Visual Studio 2019 uses #pragma once. I see it can also be used in Arduino IDE? Is that a shortcut for what is normally included at the beginning of a header file using:
#ifndef headername_h
#define headername_h
... code goes here ...
#endif