Separate Sketch Tabs

Hi all. Newbie to Arduino here, but I’m fairly experienced with C programming. I’m trying to understand if the analogy between separate tabs in Arduino sketches and separate .c files on other platforms is exact with respect to the scope of global variables and functions.

On other platforms global variables (a.k.a. external variables -- not defined within a function) and functions in one .c file can be accessed in another file by using ‘extern’ declaration and the linker will sort things out. Also, these same global variables and functions can be hidden and confined to a single file by using ‘static’ in their definition.

But, I’ve read that in Arduino all the tabs are combined into a single entity (alphabetically by tab name) before compilation. If that’s the case, then these scope rules might be altered depending on where the definitions appear in the combined entity.

Appreciate any help in understanding this.

Thanks.

Greg

Hi Also new to Arduino and I use tabs to sort functions so I can find them faster. I have 5 tabs which all combine into one when compiling. I did't have any problems with the scopes. But that might be because I declare my vars in the first (main) tab.

HTH

But, I've read that in Arduino all the tabs are combined into a single entity (alphabetically by tab name) before compilation.

That is true only if the file shown in the tab is a .ino file.

That is not true if the tab is showing a .h file, a .c file, or a .cpp file.

If that's the case, then these scope rules might be altered depending on where the definitions appear in the combined entity.

Not really. A variable is either defined in a function, where its scope is local, or outside of any function, where its scope is global.

The scope of a variable, though, is only one factor in determining where it can be used. It must, of course, be defined before it is used.

Thanks PaulS. As I said, I’m new to Arduino and wasn’t aware tabs could contain .c files. Is it documented somewhere how .ino and .c tabs compile and link?

Also, could someone please tell me why this code won’t compile:

scopeTest(.ino) Tab:

extern int dog;
extern void setDog();
void setup() {
   dog = 2;
}

void loop() {
   dog = 3;
   setDog();
}

aaa.c Tab:

int dog;
void setDog() {
  dog = 7;
}

It produces the error message: Arduino: 1.6.12 (Windows 7), Board: "Arduino/Genuino Uno" C:\Users\tr001221\AppData\Local\Temp\ccEg9non.ltrans0.ltrans.o: In function `main': ccEg9non.ltrans0.o:(.text.startup+0x96): undefined reference to `setDog()' collect2.exe: error: ld returned 1 exit status exit status 1

So, the extern declaration for the variable 'dog' is working, but not the one for the function 'setDog()'.

So, the extern declaration for the variable 'dog' is working, but not the one for the function 'setDog()'.

Incorrect. To prove that, rename the .c file to .cpp.

The reason that the linker can't find the setDog() function is that C++ allows for function overloading. For instance, Serial.print() can take an integer, a float, a string, and a String.

During compilation, the compiler actually creates 4 functions, with cryptic names, for the overloads that handle the 4 types. The process is called name mangling.

C does not support function overloading, so name mangling is not needed. The linker is looking for the name-mangled function, because the compiler said that that was the function to be linked in. Since the C compiler did not do name mangling for the setDog() function, the function that the linker is looking for does not exist.

There are ways to handle this, but the easiest is just to rename the C file to be a C++ file, so that the C++ compiler is used, and does name mangling.

I believe you could also do this:

extern "C" {
  void setDog();
}

and leave the source file as .c.

PaulS, Jiggy-Ninja:

Yes, both techniques allowed the compilation to complete error-free. Renaming the file to .cpp was a bit easier.

So, as a newbie to Arduino, I’d like to structure my projects in a similar manner (and with similar scoping rules) as I’m used to when writing C on platforms like Linux -- in separate .c files joined up by the linker after compilation. Looks like the easiest way to do this is with separate tabs having .cpp extensions, even though I’m not yet able to program in C++.

Guess I don’t really see the benefit of Arduino combining all .ino tabs into a single unit before compiling. I think it makes the scoping situation more confusing.

Thanks for your help.

Greg

even though I'm not yet able to program in C++.

C++ is a superset of C. If the only part of the superset you need is the part that is C, just use that part of the superset.

Guess I don't really see the benefit of Arduino combining all .ino tabs into a single unit before compiling.

Benefit or not, that is what it does. If you don't like that, stick with one ino file and the rest can be cpp files.

I think it makes the scoping situation more confusing.

Not if you know that that is going to happen, and the order that files will be combined. If you don't, don't have more than one ino file.

gfvalvo: Guess I don’t really see the benefit of Arduino combining all .ino tabs into a single unit before compiling. I think it makes the scoping situation more confusing.

Not everything Arduino chose to do makes sense.

Automatically generating function prototypes might seem helpful, until you try and use default parameters and it chokes on them with a cryptic compilation error. So you have to use overloading (I don't think making my own prototype fixed it).

And then you try and use function pointers, and after some hunting find that it puts those generated prototypes in the stupidest possible spot, immediately before the first function (ie, not at the very top of the page). This is usually after all the global variable definitions, and not in scope for any callback variables declarations. Now you've got to make your own prototypes above the globals section.

Now you've got to make your own prototypes above the globals section.

Or put the functions in another file, and the prototypes in a header file, where they belong.

The IDE is designed for writing blink type sketches. For things more complicated than that, writing code properly avoids all the IDE "help".

PaulS: Benefit or not, that is what it does. If you don't like that, stick with one ino file and the rest can be cpp files.

OK, let me rephrase that as a question. Can someone explain the philosophy behind this behavior? What are the usage scenarios envisioned by Arduino’s architects where the “combine then compile” behavior of .ino files is advantageous verses the more conventional “compile separately then link” behavior of .c and .cpp files?

Thanks.

Greg

gfvalvo: OK, let me rephrase that as a question. Can someone explain the philosophy behind this behavior? What are the usage scenarios envisioned by Arduino’s architects where the “combine then compile” behavior of .ino files is advantageous verses the more conventional “compile separately then link” behavior of .c and .cpp files?

Easier for beginners.

But it actually creates bad habits and when somebody that learned C/C++ on an Arduino enters the 'real world' he/she will have a hard time with error messages about unknown functions and variables.

sterretje: Easier for beginners.

But it actually creates bad habits and when somebody that learned C/C++ on an Arduino enters the 'real world' he/she will have a hard time with error messages about unknown functions and variables.

Creating bad coding habits is one thing, perpetuating and directly encouraging them by claiming that Arduino is in business of educating is another. The old mushroom philosophy - keep the poor beginners on their level forever, in the dark... that is where the money is. Sorry for the outburst, but I just cannot stand this "let's spoon feed the poor ignorant beginner" attitude. Let then grow up. Software can be rewarding and fun outside kindergarten.

Jim