Notifications
Clear all

Platform.io one annoying feature

9 Posts
4 Users
0 Reactions
4,737 Views
(@pugwash)
Sorcerers' Apprentice
Joined: 6 years ago
Posts: 923
Topic starter  

These are only my personal preferences but 1. I like to have my setup() and loop() functions at the top of my sketches, and 2. for ease of navigation order my functions alphabetically.

Platform.io will not compile unless the functions are between the header files and the setup() function.

There is a workaround and that is to declare all the functions between the header files and setup() but write the function's code below the loop() section. I don't know why the platform.io developers decided to implement in this manner but the C++ compiler doesn't care where the functions are situated.


   
Quote
Topic Tags
(@zeferby)
Member
Joined: 5 years ago
Posts: 355
 
Posted by: @pugwash

I don't know why the platform.io developers decided to implement in this manner

I've always written my C/C++ stuff this way in the distant past (no c++ between 2004 and 2019) : declare first, define later; with usually my declarations in .h files and my definitions in the cpp files.

Actually the permissiveness of the Arduino IDE (and other compilers ?) surprised me.

I'm a declarative type of guy ? 

Edit :

After following some links on the Web for something else i stumbled upon this, which explains some forgiveness from the Arduino IDE :

https://github.com/arduino/Arduino/wiki/Build-Process#pre-processing

 

Eric


   
ReplyQuote
frogandtoad
(@frogandtoad)
Member
Joined: 6 years ago
Posts: 1458
 

@pugwash

I tried platform IO quite a while back, but found it too awkward, though I do like my VSCode editor and use it a lot!  Maybe I will give platform IO another go at some stage.

I did try the new Arduino IDE in Alpha.  It looks promising and I look forward to it's final release - The dark theme is nice, but not quite the same as my Monokai Vibrant on VSCode 🙂

Posted by: @pugwash

Platform.io will not compile unless the functions are between the header files and the setup() function.

This is actually the norm in every IDE I've ever worked with - Forward declarations are always required.  Like ZeFerby, I too was surprised how the Arduino IDE allowed it, but they may well just be forward declaring behind the scenes for you, or some other kind of compiler trickery! 🙂


   
ReplyQuote
Robo Pi
(@robo-pi)
Robotics Engineer
Joined: 6 years ago
Posts: 1669
 
Posted by: @frogandtoad

I too was surprised how the Arduino IDE allowed it, but they may well just be forward declaring behind the scenes for you, or some other kind of compiler trickery! ?

Yes, that's exactly what it does.  It's simply a multi-pass compiler.  It goes thought the code first to obtain all the declarations, and then complies it after that.    I like to define my functions at the end of my main code.  But as has been pointed out some compilers force the user to put them at the top. 

I've been working with OpenCV on the Jetson Nano and discovered that it works this way too.  I had defined a method in a program and referenced before it had been defined.  It wouldn't compile.   It didn't even say exactly what the problem was.  It just pointed to the line were the function was called and said, "function undefined"   I knew that was baloney because the function was actually defined on the very next line! 

But nope the compiler doesn't look even so much as one line ahead.  I had to move the function above where I'm calling it from.

This has nothing to do with coding in general or languages in general.   This is entirely a compiler characteristic.  Some compilers allow functions to be placed at the end of a program.  Other won't.

To be certain you can always place the function definitions at the top, then all compilers will be sure to compile them without a problem.  I've been in the habit for years of placing my function methods at the bottom of the code text editor.

Another thing you can do is to place all your functions in a separate tabbed class called "Functions",  Then they'll always be there when you import or include your Functions class.

 

DroneBot Workshop Robotics Engineer
James


   
ReplyQuote
frogandtoad
(@frogandtoad)
Member
Joined: 6 years ago
Posts: 1458
 

@robo-pi

Posted by: @robo-pi

This has nothing to do with coding in general or languages in general.   This is entirely a compiler characteristic.  Some compilers allow functions to be placed at the end of a program.  Other won't.

I wouldn't say that's entirely true, simply because programs are generally read in a top down fashion, and so parsed sequentially.  Obviously you can parse the code twice, but then you loose compiler efficiency, especially in large programs, just for the sake of recording and determining the symbols, so there is a trade off, but most likely not such a big deal in embedded programming, as programs are usually small as can be.

Posted by: @robo-pi

Another thing you can do is to place all your functions in a separate tabbed class called "Functions",  Then they'll always be there when you import or include your Functions class.

Not sure if you've just stated it incorrectly, but the proper way is to us a header and implementation file (a .h (header) file, with an associated .cpp (implementation) file).  I have posed examples of such code in the past, and this is the best way to do it when your programs start to become larger in size.


   
ReplyQuote
Robo Pi
(@robo-pi)
Robotics Engineer
Joined: 6 years ago
Posts: 1669
 
Posted by: @frogandtoad

Not sure if you've just stated it incorrectly, but the proper way is to us a header and implementation file (a .h (header) file, with an associated .cpp (implementation) file).  I have posed examples of such code in the past, and this is the best way to do it when your programs start to become larger in size.

Yes that's what I'm talking about.  My post wasn't intended to be a tutorial. ?  You need to take care of all the details.  My point is that if you don't like having your functions at the beginning of your code you can move them to a separate tab.   Or, as you point out, to a separate "two tabs".

DroneBot Workshop Robotics Engineer
James


   
ReplyQuote
frogandtoad
(@frogandtoad)
Member
Joined: 6 years ago
Posts: 1458
 

@robo-pi

Posted by: @robo-pi

My point is that if you don't like having your functions at the beginning of your code you can move them to a separate tab.   Or, as you point out, to a separate "two tabs".

Just to clarify for newbies new to programming... tabs are something an IDE (for example, an Arduino IDE) displays for ease of access to your individual programming files, and not part of the language.


   
ReplyQuote
(@zeferby)
Member
Joined: 5 years ago
Posts: 355
 

So, reading the  Arduino Wiki on Github about the Arduino IDE pre-processing phase during the Build Process, I could not take it at face value and created a small project (see scratch001.zip) to check if these statements are still valid with Arduino IDE 1.8.10 :

The Arduino environment performs a few ? transformations to your sketch before passing it to the avr-gcc compiler:

  • All .ino files in the sketch folder (shown in the IDE as tabs with no extension) are concatenated together, starting with the file that matches the folder name followed by the others in alphabetical order, and the .cpp extension is added to the filename.
  • If not already present, #include <Arduino.h> is added to the sketch. This header file (found in the core folder for the currently selected board) includes all the definitions needed for the standard Arduino core.
  • Prototypes are generated for all function definitions in .ino files that don't already have prototypes. In some rare cases prototype generation may fail for some functions. To work around this, you can provide your own prototypes for these functions.
  • #line directives are added to make warning or error messages reflect the original sketch layout.

No pre-processing is done to files in a sketch with any extension other than .ino. Additionally, .h files in the sketch are not automatically #included from the main sketch file. Further, if you want to call functions defined in a .c file from a .cpp file (like one generated from your sketch), you'll need to wrap its declarations in an 'extern "C" {}' block that is defined only inside of C++ files.

Result : check passed ! All of that is still true with Arduino IDE 1.8.10.

(the only thing i did not bother to check is "extern C", which is standard in mixed C/C++ projects)

 

Using verbose output for compile/link in Arduino IDE, we can find the temporary folder where the IDE is building the project, and find all the relevant files there : first pass and second pass arduino-"preprocessed"/contatenated global ino-cpp source, dependencies, object files, generated firmware.

This is the resulting scratch001.ino.cpp for my little scratch001 project :

#include <Arduino.h>
#line 1 "E:\\EFMesDocs\\Arduino\\MySketches\\scratch001\\scratch001.ino"
#include "utilheader.h"
#include "other_cpp_module.h"

unsigned long ulTime, ulT0;

#line 6 "E:\\EFMesDocs\\Arduino\\MySketches\\scratch001\\scratch001.ino"
void setup();
#line 13 "E:\\EFMesDocs\\Arduino\\MySketches\\scratch001\\scratch001.ino"
void loop();
#line 25 "E:\\EFMesDocs\\Arduino\\MySketches\\scratch001\\scratch001.ino"
void myUndeclaredFunction();
#line 3 "E:\\EFMesDocs\\Arduino\\MySketches\\scratch001\\myfile002.ino"
void myOtherUndeclaredFunction();
#line 6 "E:\\EFMesDocs\\Arduino\\MySketches\\scratch001\\scratch001.ino"
void setup() {
  Serial.begin(11520);
  Serial.print("setup() from :\t");
  Serial.println(__FILENAME__);
  ulT0 = millis();
}

void loop() {
  ulTime = millis();
  if ((ulTime - ulT0) > 1000) { //only output once per second
    ulT0 = ulTime;
    Serial.print("loop() from :\t");
    Serial.println(__FILENAME__);
    myUndeclaredFunction();
    myOtherUndeclaredFunction();
    myProperlyDeclaredFunction();
  }
}

void myUndeclaredFunction() {
    Serial.print("myUndeclaredFunction() from :\t");
    Serial.println(__FILENAME__);
}

#line 1 "E:\\EFMesDocs\\Arduino\\MySketches\\scratch001\\myfile002.ino"
#include "utilheader.h"

void myOtherUndeclaredFunction() {
  Serial.print("myOtherUndeclaredFunction() from :\t");
  Serial.println(__FILENAME__);
}


Eric


   
ReplyQuote
(@zeferby)
Member
Joined: 5 years ago
Posts: 355
 

As you can guess, the same project in a more conventional C++ IDE like VSCode+PlatformIO (with all ino files renamed to cpp), required the usual prototype declaration for 

void myUndeclaredFunction();

and a header file for myfile002, with a prototype declaration of

void myOtherUndeclaredFunction();

image

Eric


   
ReplyQuote