1. Location of setup() and loop(); 2. Passing arguments by reference

I understand the pre-processor does function declarations...
C++ allows that no function declaration is made (before main()), IF the functions are defined before they are being used.
Would it therefore be a good move / practice, to write functions first and then have setup() and loop() last?

Also Passing arguments by reference
I have been wondering, and actually may do not understand this properly...

I want to write a function serialPrint() which uses two arguments (itemName and itemValue) [and does some formatting of the output].
While the itemName is always a string, the itemValue can be any type.
What do I need to put into the function / parameter braces?
e.g. serialPrint(String itemName, ??? itemValue)

I understand the pre-processor does function declarations...

It does not. The IDE does, when it can, before it invokes the compiler which has a preprocessor phase.

C++ allows that no function declaration is made (before main()),

It also allows them to be declared before main().

Would it therefore be a good move / practice, to write functions first and then have setup() and loop() last?

In my opinion, that is a piss-poor idea. I KNOW that your program starts in setup() and then calls loop() over and over. To see what your program is doing, I want to see setup() and loop() at the top. The compiler doesn't care, so humor me.

I want to write a function serialPrint() which uses two arguments (itemName and itemValue) [and does some formatting of the output].
While the itemName is always a string, the itemValue can be any type.

The Serial object is an instance of the HardwareSerial class, which derives from Print. Look at how Print declares, and implements, the print() method. There are no shortcuts.

The Arduino environment sits in front of the preprocessor. It puts in function declarations for you. So it should not matter which order you write the functions. (There are some unusual cases where the order is important but for 99% of the programs I write, it isn't.)

You can have functions which accept differently typed parameters. But the easiest way is to overload the function. For example, you may only need to accept int and float parameters for itemValue. So you write the function twice, using the two different definitions.

void serialPrint(String itemName, int itemValue) {
...
}
void serialPrint(String itemName, float itemValue) {
...
}

The compiler knows which version to call when you call it because it sees the type of the variable you used on the right-hand side and sends that call to the correct version.

MorganS:
The compiler knows which version to call when you call it because it sees the type of the variable you used on the right-hand side and sends that call to the correct version.

Well well well, we live and learn!

Does that voodoo extend to the number of arguments?

void myFunction(int a, int b, int c)
{
//do stuff with a, b and c
}

void myFunction(int a, int b)
{
//do stuff with a and b, no c this time
}

Apologies for going OT a bit.

Does that voodoo extend to the number of arguments?

There is no voodoo involved. Google "name mangling" to understand just how C++ manages function overloading.

PaulS:
It does not. The IDE does, when it can, before it invokes the compiler which has a preprocessor phase.

What I meant...

PaulS:
It also allows them to be declared before main().

Also what I meant... a declaration is not required if functions are defined before main() or before being called.

PaulS:
In my opinion, that is a piss-poor idea.

... to be able to run the program in other IDEs or compilers... with regard to my previous statement.

PaulS:
The Serial object is an instance of the HardwareSerial class, which derives from Print. Look at how Print declares, and implements, the print() method. There are no shortcuts.

I do not understand, sorry...

...

MorganS:
You can have functions which accept differently typed parameters.

Thank you for your nice and helpful explanation!

I have, in the meantime created the function below, to help with debugging and display the result, which I did previously like so:

    Serial.print(F("IP Address client ...........: "));
    Serial.println(Ethernet.localIP());
#include <SPI.h>                                  // required for EtherShield
#include <Ethernet.h>                             // Ethernet Library for EtherShiled

byte MAC_ADDRESS[] = { 0x4D, 0x61, 0x78, 0x47, 0x39, 0x37 };  // 6 hex digit MAC: reads MaxG97
EthernetClient ethClient;                         // Ethernet

void setup() {
  Serial.begin(9600);
}

void loop() {
  while(1) {
    void serialPrint("IP Address client", Ethernet.localIP());
  }
}

void serialPrint(String itemName, char itemValue) {

  byte itemColumnLength = 50;
  byte itemNameLength   = itemName.length();
  byte numberOfDots     = itemColumnLength - itemNameLength - 2;
  String dots           = "";

  for (byte i = 0; i <= numberOfDots; i++) {
    dots = dots + ".";
  }

  Serial.println(F(String(itemName + " " + dots + ": " + itemValue + "\n")));
}

However, it complains by saying this:

D:\MaxG_MyDocuments\Arduino\zTests\serialPrintV0\serialPrintV0.ino: In function 'void loop()':

serialPrintV0:13: error: variable or field 'serialPrint' declared void

     void serialPrint("IP Address client", Ethernet.localIP());

                     ^

In file included from C:\Users\MaxG\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.16\cores\arduino/Arduino.h:28:0,

                 from sketch\serialPrintV0.ino.cpp:1:

D:\MaxG_MyDocuments\Arduino\zTests\serialPrintV0\serialPrintV0.ino: In function 'void serialPrint(String, char)':

C:\Users\MaxG\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.16\cores\arduino/WString.h:38:74: error: initializer fails to determine size of '__c'

 #define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

                                                                          ^

D:\MaxG_MyDocuments\Arduino\zTests\serialPrintV0\serialPrintV0.ino:28:18: note: in expansion of macro 'F'

   Serial.println(F(String(itemName + " " + dots + ": " + itemValue + "\n")));

                  ^

C:\Users\MaxG\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.16\cores\arduino/WString.h:38:74: error: array must be initialized with a brace-enclosed initializer

 #define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

                                                                          ^

D:\MaxG_MyDocuments\Arduino\zTests\serialPrintV0\serialPrintV0.ino:28:18: note: in expansion of macro 'F'

   Serial.println(F(String(itemName + " " + dots + ": " + itemValue + "\n")));

                  ^

exit status 1
variable or field 'serialPrint' declared void

Passing Strings around is a piss pore idea :wink: Just stay with strings.

And another thing you now don't do is to place the strings in PROGMEM (which you do with the F() macro). Something that's very useful to save RAM.

And that brings us to you wanting to use F() in a function with variables. Sorry, you can't... F() macro only works if you use it with a constant.

So yeah, it takes some work. Make the function also accept const __FlashStringHelper object made by the F() macro and drop String.

septillion:
So yeah, it takes some work.

Thanks... :slight_smile:
I scrapped that idea completely.

PaulS:
There is no voodoo involved.

Well I call it voodoo, same as I can talk on HF long path 3/4 round the world with <50W and 10m of wire in a tree; that's definitely voodoo. But I digress.

Just wrote a quick sketch to test it. So sorry to have troubled your honour.

But it's not that hard. Like Paul said, have a look at the Print class.

After a small edit it would look like:

void debugPrintTest(const __FlashStringHelper *in, int number){
  //printing the PROGMEM string, (almost) copy past from Print Class
  PGM_P p = reinterpret_cast<PGM_P>(in);
  size_t itemLength = 0;
  while (1) {
    unsigned char c = pgm_read_byte(p++);
    if (c == 0) break;
    itemLength += Serial.write(c);
  }
  
  //Now we know the lenght, fill "the rest" with dots
  for(byte i = 0; i < (ItemColumnLengh - itemLength); i++){
    Serial.write('.');
  }
  
  //followed by : and the value
  Serial.write(':');
  Serial.println(number);
}

And call it like:

debugPrintTest(F("Bla bla bla"), 5);

Or, to make it more universal to use with more types, split it:

void debugPrintTest(const __FlashStringHelper *in){
  //printing the PROGMEM string, (almost) copy past from Print Class
  PGM_P p = reinterpret_cast<PGM_P>(in);
  size_t itemLength = 0;
  while (1) {
    unsigned char c = pgm_read_byte(p++);
    if (c == 0) break;
    itemLength += Serial.write(c);
  }
  
  //Now we know the lenght, fill "the rest" with dots
  for(byte i = 0; i < (ItemColumnLengh - itemLength); i++){
    Serial.write('.');
  }
  
  //followed by :
  Serial.write(':');
}

inline void debugPrintTest(const __FlashStringHelper *in, int number){
  debugPrintTest(in);
  Serial.println(number);
}

//now it's easy to extend to float for example
inline void debugPrintTest(const __FlashStringHelper *in, float number){
  debugPrintTest(in);
  Serial.println(number);
}
void loop() {
  while(1) {
    void serialPrint("IP Address client", Ethernet.localIP());
  }
}

First, why are you using an infinite loop inside an infinite loop()?
Second, why do you have an incorrectly defined function prototype inside loop(). That is NOT a call to a function.

PaulS:
First, ...?
Second...?

It is called tired... it does all sorts of weird things to you :slight_smile:

septillion:
But it's not that hard. Like Paul said, have a look at the Print class.
...

Thank you kindly for having a crack at this...
I will try it...
... and try to make sense out of it... at first glance it looks too advanced for me.
e.g. __FlashStringHelper *in
(I need to understand code before I use it) :slight_smile:

Paul calls things "piss poor" so often, I've started to wonder if he has a prostate condition...

--Michael

__FlashStringHelper is the name of an "object" in PROGMEM (which you do with F() macro). The * as always indicates it's a pointer to it. Actually it's just a pointer to memory but the compiler normally sees no difference in a pointer to something in RAM or ROM. But it needs a different way of using it. By calling it __FlashStringHelper the compiler can identify it as in PROGMEM.

MaxG:
(I need to understand code before I use it) :slight_smile:

Mehh, not always :wink: You don't know how Serial.print() works either and you do use it. And this is just a slightly edited version of it.

Okay, it's good to try to understand what code does. But like I said, it's just a copy of .print() :wink:

septillion:
Okay, it's good to try to understand what code does. But like I said, it's just a copy of .print() :wink:

Yes, I hear / get what you are saying (and do not mean that in a negative way) ... and now get what another poster said: 'look at print()' ... I didn't get look at the source :slight_smile:
Like my signature says, everything needs to be defined :slight_smile:
I look at other code and learn from it, but tend to use it when I understand it, as I have to maintain and thus understand what it is. I treat libraries or the language differently, as in: it is there use it, without wondering what is under the hood.
I have not enough skill to read the more advanced stuff... and do not understand pointers...

When I started with Arduino, yes, I did copy at first, but paid a relatively high price due to the shortcomings of the code I copied (as I could not distinguish between good and bad).

I got this idea for this function when I was trying to improve / rewrite my code, and thought why not use a function for this sort of debug code (something I had done in PHP before, e.g. if debug flag show function call, variable, value etc.)

In any case: I do thank you for your constructive help! :slight_smile:

Then my advice, learn a bit about pointer. Because actually you are already using them if you use arrays (including c strings aka char arrays) :wink:

And if you used PHP5 you might already know a bit about them. :wink:

<?php

class foo {
    public $bar;
}


$a = new foo();
$a->bar = "I'm var A";

print($a->bar); //prints "I'm var A"

$b = $a;
$b->bar = "I'm var B";

print($a->bar); //Now this also prints "I'm var B"!

?>

And that's because $a and $b are pointers to the object and $b points to the same object as $a