Rename "Serial" to something else?

I'd like to rename the "Serial" thing ... or I guess I'd like to "store a reference to it" in another variable.
But I have a really hard time finding out what Serial even is. Is serial a class with static methods or is it actually an instance? I'm not too familiar with C++...

In the end I'd like to have two variables "ComputerSerial" and "BluetoothSerial".
And I'd like to be able to set those variables to either a hardware Serial or a SoftwareSerial.

So basically this is what I tried:

SoftwareSerial BluetoothSerial(9,10);
HardwareSerial ComputerSerial = Serial;

void MyFunc(Stream &SerialObj) {
    SerialObj.println("test");
}

void setup() {
  ComputerSerial.begin(115200); 
  BluetoothSerial.begin(115200);
}

void loop() {
  MyFunc(ComputerSerial); //send "test" to the computer serial
  MyFunc(BluetoothSerial); //send "test" to the bluetooth serial
}

I used "Stream" as the datatype for the function parameter because I read somewhere that both HardwareSerial and SoftwareSerial are actually just "Stream"s. This way I can pass any kind of Serial object(?) to the function. And if I ever device to switch a serial connection from hardware to softawre or the other way around, I just need to change the first two lines of the code and the rest can stay the same.

But I'm experiencing very weird behavior with this that I cannot explain...
For example the output of this code:

void setup() {
  ComputerSerial.begin(115200); 
  ComputerSerial.println("test");
}

It only prints "te".
If I change ComputerSerial to Serial it sends "test".

Any idea how I could make this work properly?

auto& ComputerSerial = Serial;

You need the &.

I haven't tried it on my Arduino, but I did it on a desktop program with ints and it worked.

#include <iostream>
#include <iomanip>

using namespace std;

int main(int argc, char** argv) {
	int alpha = 5;
	auto& bravo = alpha;
	
	cout << "alpha before reassignment: " << alpha << endl;
	bravo = 10;
	cout << "alpha after reassignment: " << alpha << endl;
	
	return 0;
}

Output:

alpha before reassignment: 5
alpha after reassignment: 10

I used "Stream" as the datatype for the function parameter because I read somewhere that both HardwareSerial and SoftwareSerial are actually just "Stream"s. This way I can pass any kind of Serial object(?) to the function. And if I ever device to switch a serial connection from hardware to softawre or the other way around, I just need to change the first two lines of the code and the rest can stay the same.

This is exactly the kind of forward thinking that prevents headaches later on. Well done.

HardwareSerial ComputerSerial = Serial;

You can't copy the serial object like that. But you can make a pointer to it.

HardwareSerial *ComputerSerial = &Serial;

You are correct that functions which accept a pointer to a serial object should use the Stream class. That will work for SoftwareSerial too.

Do I really need the auto thing? Is it a datatype? Could I just use Stream instead?

And about that:
HardwareSerial *ComputerSerial = &Serial;

Does this result in such a ppointer that my syntax changes from dots to arrows? I mean ComputerSerial->print() instead of ComputerSerial.print()?

Yes, it's a pointer so you change dots to arrows. You were going to do that anyway inside the functions which take a Stream pointer.

Please, please use a reference. It is like a pointer, but you don't have to use the arrow notation:

    HardwareSerial & bluetooth = Serial2; // an alias

So much cleaner! It's also much easier to search and replace the Serial name, unlike replacing the type and its usage. :stuck_out_tongue: Function arguments can use the same technique:

void foo( Stream & stream )
{
  if (stream.available()) {
   ...
}

Cheers,
/dev

felic:
Do I really need the auto thing? Is it a datatype? Could I just use Stream instead?

And about that:
HardwareSerial *ComputerSerial = &Serial;

Does this result in such a ppointer that my syntax changes from dots to arrows? I mean ComputerSerial->print() instead of ComputerSerial.print()?

auto is a special C++11 keyword that lets you declare a variable without having to care about looking up what type it needs to be, the compiler will deduce that for you based on the initialization statement. When you do:

auto& ComputerSerial = Serial;

The compiler creates a reference of the same type as the Serial object. You don't need to tell it what type it is, since the compiler will already know. If you change it to a reference to a SoftwareSerial, it will create a reference to a SoftwareSerial object the next time you compile it. The typing is handled automatically.

You can use a pointer for this, but since you are not doing any pointer arithmetic (ie. iterating over an array) or using dynamic memory, a reference has less things that can go wrong. You don't need to remember to *dereference it or use that -> thing.

Alright, thank you so much! Works like a charm.

Is it save to provide other Arduino users with C++11 code yet or is C++11 support a very recent change in the software?

MorganS:
You can't copy the serial object like that. But you can make a pointer to it.

...

You are correct that functions which accept a pointer to a serial object should use the Stream class. That will work for SoftwareSerial too.

How about a reference to it?

class myMenu 
  {
  private:
    Stream & port_; 
  public:
    myMenu (Stream & port) : port_ (port) { }
    void begin ();
  };

void myMenu::begin ()
  {
  port_.println ("Menu initialized."); 
  }

myMenu menu (Serial);

void setup ()
  {
  Serial.begin (115200);
  menu.begin ();
  }  // end of setup

void loop () { }

felic:
Alright, thank you so much! Works like a charm.

Is it save to provide other Arduino users with C++11 code yet or is C++11 support a very recent change in the software?

It works in 1.6.9, I don't know how much farther back it goes since I've been away from Arduino for a while.

I turned on verbose compile output and it shows the flag -std=gnu++11.

I thought the auto storage specifier could only be used with a variable defined with local or block scope and assumes the default storage class for the compiler (e.g., int) if there's no initializer. However, it appears that you can define an auto with global scope, and the definition must have an initializer list. Can anyone state what the rules are?

econjack:
I thought the auto storage specifier could only be used with a variable defined with local or block scope and assumes the default storage class for the compiler (e.g., int) if there's no initializer. However, it appears that you can define an auto with global scope, and the definition must have an initializer list. Can anyone state what the rules are?

Global variables.
Local/Static variables.

auto x = 4; //int
auto a = new auto(1); //int, 'a' is int*

if(auto p = func()){
  //...
}

Function return types.

template< typename T, typename U >
auto func( T t, U u ) -> decltype(u+t){
  return t + u;
}

Return types for lambdas:

int j;
auto x3 = []()->auto&& { return j; };

Arduino can only utilize C++11 so far, but for future reference, in C++14 generic lambdas have been added, and also function parameters can be auto.

[](auto t){ return t; }

void func( auto a, auto b ){

}

As for the rules, there is too much to copy, format, and paste.

Just check section "7.1.6.4 auto specifier" in the standard here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf

Thanks, pYro_65!