Giving back to the community

I am a self taught C++ Arduino compatible programmer. As such, I get by without knowing the full range of topics in this language. However, I have written many useful programs over the years.

Recently, I wanted to learn about pointers and libraries. Excellent articles exist and I studied many. However, I still struggled with jargon plus unanswered questions. The Arduino forum was helpful but sometimes I just had to experiment to find out what worked. The danger is that I could easily come to the wrong conclusion or do something that was not a best practice.

The journey was well worth my effort. I now want to make the way a bit smoother for those that follow. The attached article is my first attempt. I'm looking to experts to point out mistakes I have made so they can be quickly corrected. The last thing I would want to do is trip up a fellow traveler.

Thanks,

Rick

Pointers.pdf (656 KB)

An lvalue is a value that can appear on the left of an assignment.
It is left (no pun) as an exercise to figure out what an rvalue is.

TheMemberFormerlyKnownAsAWOL:
An lvalue is a value that can appear on the left of an assignment.
It is left (no pun) as an exercise to figure out what an rvalue is.

Now, in to the deep, dark pit of Move Semantics .....

TheMemberFormerlyKnownAsAWOL:
An lvalue is a value that can appear on the left of an assignment.
It is left (no pun) as an exercise to figure out what an rvalue is.

I sure hope you are not saying that an lvalue is just something on the left side of an equation and an rvalue is something on the right side of an equation.
Rick

Equation?

"I have experimentally confirmed that I get compiler errors if each class name does
not start with a capital. "

This is a surprise to me. Show us your experiments.

And this:

"In general, I treat compiler errors related to a library as telling me I did something
wrong. I do not try to make sense of its details."

Seems like an unusual philosophy. If you were old enough to have dealt with compilers that offered little to no information, you might appreciate that these error messages have meaning and can cut the work of correcting a program down to size.

Just sayin'.

a7

there are definitely things to fix, but kudos for taking the time to share your notes.

I have experimentally confirmed that I get compiler errors if each class name does
not start with a capital.

here is an example of class names starting with non capital letters and all is working fine

class box {
  public:
    uint8_t val;
    box(uint8_t v) : val(v) {}    // constructor
    uint8_t value() {return val;}
};

class cat {
  public:
    uint8_t val;
    cat(uint8_t v) : val(v) {}    // constructor
    uint8_t value() {return val;}
};

class dog {
  public:
    uint8_t val;
    dog(uint8_t v) : val(v) {}    // constructor
    uint8_t value() {return val;}
};

box aBox(10);
cat aCat(20);
dog aDog(30);

void setup() {
  Serial.begin(115200);
  Serial.print(F("aBox -> ")); Serial.println(aBox.value());
  Serial.print(F("aCat -> ")); Serial.println(aCat.value());
  Serial.print(F("aDog -> ")); Serial.println(aDog.value());
}

void loop() {}

it's just a convention to name the class starting with a capital letter. The language is case sensitive, so Box and box are two different things.

I was unable to find a way to have a constructor with no arguments that would compile without errors using the Arduino IDE.

here is an example of such a constructor with no argument

class Box
{
  public:
    uint8_t val;
    
    Box() {val = 0;}    // constructor
    void    setVal(uint8_t v) {val = v;}
    uint8_t getVal() {return val;}
};

Box aBox;

void setup() {
  Serial.begin(115200);
  aBox.setVal(33);
  Serial.print(F("aBox -> ")); Serial.println(aBox.getVal());
}

void loop() {}

I empirically found that the constructor must not be inside of any function. For example, if I call the constructor within setup(), the compiler will complain that it doesn’t recognizer any of the members of its class.

here is an example of dynamic instantiation, within the setup with a global pointer

class Box
{
  public:
    uint8_t val;
    
    Box() {val = 0;}    // constructor
    void    setVal(uint8_t v) {val = v;}
    uint8_t getVal() {return val;}
};

Box* aBox;

void setup() {
  Serial.begin(115200);
  aBox = new Box;
  aBox->setVal(33);
  Serial.print(F("aBox -> ")); Serial.println(aBox->getVal());
}

void loop() {}

or here is an example of just dynamic instantiation of a class instance that is local to the setup() function

class Box
{
  public:
    uint8_t val;
    
    Box() {val = 0;}    // constructor
    void    setVal(uint8_t v) {val = v;}
    uint8_t getVal() {return val;}
};

void setup() {
  Box aBox;
  Serial.begin(115200);
  aBox.setVal(33);
  Serial.print(F("aBox -> ")); Serial.println(aBox.getVal());
}

void loop() {}

all your empirically found knowledge is based on misconceptions or failure to get things done the right way. You should read a proper C++ tutorial.

In general, I treat compiler errors related to a library as telling me I did something
wrong. I do not try to make sense of its details

Yes, compiler errors means there is something wrong. But making sense of the details can definitely help you understand what got wrong in the first place. (I reckon sometimes the compiler is cryptic)

Again, kudos and +1 karma for taking the time to jot that down, and expose your work here to the community - not always the most tender one for weird claims :slight_smile: ... bare with us, constructive feedback and criticism is something positive.

I have only read as far as page 11 and I would like to make the following comments.

I am also a self taught programmer. However I started my programming experience with some IBM 360 Assembler programming so the concept of pointers has never been a problem for me.

I am by no means an expert in C/C++ but I am interested in the idea of clear explanations for newbies so let me stick my neck out even at the risk of having my head bitten off :slight_smile:

Let me start by saying that your general approach seems very useful. (Of course a sentence like that is always followed with a BUT)

I think your use of the word lvalue is fundamentally flawed. It has a much wider meaning than the way you are using it. For the purpose of a tutorial on pointers why not use the word address which is what you are really talking about.

On page 5 you have an image that includes this
&MyHatSize = 28123
that immediately gives the impression that the value 28123 is being assigned to the variable &MyHatSize. I realise that is not what you intended. Maybe this would be better
&MyHatSize has the value 28123

But even then I run into a problem because &MyHatSize is not an "item". It can also be written
& MyHatSize where it is clearer that the & is an operator telling the compiler to get the address of the variable MyHatSize.

I have the same sort of problem with this from page 8
byte *_MyArray = &MyArray[0];
MyArray[]was defined as both byte and an array. _MyArray is only defined
as a byte
Again, the * is an operator. It could also be written as byte
_MyArray or byte * _MyArray. The construct byte *_MyArray creates a pointer variable called _MyArray which holds the address of a variable that was defined as a byte.

Then it gets more confusing because you can also use the * as an operator to tell the compiler to use the value at the address. For example byte johnsHatValue = * _MyHatValue. I don't know if you have covered this on a later page, but it seems to me to be relevant at the early part of the Tutorial.

No doubt if I have got this all wrong there will be no shortage of people to correct me.

I have always found the treatment of pointers in C/C++ confusing because the characters used for the operators give no indication of their purpose and the * is also used for multiplication (giving it 3 different uses), just to confuse things further. They could easily have used addressof() and valueat()

...R

They could easily have used addressof() and valueat()

that was costly in paper when archiving your programs on punched card and in memory for compiling or just parsing. 1 letter operators that can be told apart by syntax/grammar rules were good enough.

There was a time when those things mattered.

for the French speaking community I've published a memory/pointer introduction here

@J-M-L:

"nos petits Arduinos"

mort de rire!

a7

h

alto777:
"nos petits Arduinos"

they are small indeed (both in size and memory) :slight_smile:

TheMemberFormerlyKnownAsAWOL:
Equation?

assignment?
Rick

"They could easily have used addressof() and valueat()"

But they didn't. So we learn C, get over it, and move on. I know you have!

Besides really gumming up some common and other not so common idioms with excess "function" orientated syntax, addressof/valueat opens the question of how variable declarations and function prototypes would look.

One of C's strengths is the strong resemblance between declaration and use. Everywhere. Once internalized, the constructs are easily recognized, read and written.

C++, as far as I have learned anything about it, has sorta dropped that ball. And don't get me started on operator overloading, which makes C's *, &, ||, &&, ++, <<= &c. (&c., haha!) feel like no kind of problem at all.

a7

alto777:
But they didn't. So we learn C, get over it, and move on. I know you have!

I have. And I did not expect the more experienced users here to waste the OP's time discussing my peccadillos or the early history of the C language.

I was just using an example to bring to the OP's attention the fact that the way pointers are handled in C/C++ can be confusing even though the concept of pointers is straightforward.

...R

alto777:
"I have experimentally confirmed that I get compiler errors if each class name does
not start with a capital. "

This is a surprise to me. Show us your experiments.

a7

I went back and discovered that it does compile without errors. This is a relieve because it made no sense to me as I had stated it.
I do have an example where I forgot the ";" at the end of my class definition. It caused compiler errors pointing to my .ino file that made no sense to me. See page 36.
Thank you so much for your help. I continue to work through all of the comments I received.
Rick

J-M-L:
there are definitely things to fix, but kudos for taking the time to share your notes.

here is an example of class names starting with non capital letters and all is working fine

class box {

public:
   uint8_t val;
   box(uint8_t v) : val(v) {}    // constructor
   uint8_t value() {return val;}
};

class cat {
 public:
   uint8_t val;
   cat(uint8_t v) : val(v) {}    // constructor
   uint8_t value() {return val;}
};

class dog {
 public:
   uint8_t val;
   dog(uint8_t v) : val(v) {}    // constructor
   uint8_t value() {return val;}
};

box aBox(10);
cat aCat(20);
dog aDog(30);

void setup() {
 Serial.begin(115200);
 Serial.print(F("aBox -> ")); Serial.println(aBox.value());
 Serial.print(F("aCat -> ")); Serial.println(aCat.value());
 Serial.print(F("aDog -> ")); Serial.println(aDog.value());
}

void loop() {}



it's just a convention to name the class starting with a capital letter. The language is case sensitive, so Box and box are two different things. 

here is an example of such a constructor with no argument


class Box
{
 public:
   uint8_t val;
   
   Box() {val = 0;}    // constructor
   void    setVal(uint8_t v) {val = v;}
   uint8_t getVal() {return val;}
};

Box aBox;

void setup() {
 Serial.begin(115200);
 aBox.setVal(33);
 Serial.print(F("aBox -> ")); Serial.println(aBox.getVal());
}

void loop() {}




here is an example of dynamic instantiation, within the setup with a global pointer 


class Box
{
 public:
   uint8_t val;
   
   Box() {val = 0;}    // constructor
   void    setVal(uint8_t v) {val = v;}
   uint8_t getVal() {return val;}
};

Box* aBox;

void setup() {
 Serial.begin(115200);
 aBox = new Box;
 aBox->setVal(33);
 Serial.print(F("aBox -> ")); Serial.println(aBox->getVal());
}

void loop() {}




or here is an example of just dynamic instantiation of a class instance that is local to the setup() function


class Box
{
 public:
   uint8_t val;
   
   Box() {val = 0;}    // constructor
   void    setVal(uint8_t v) {val = v;}
   uint8_t getVal() {return val;}
};

void setup() {
 Box aBox;
 Serial.begin(115200);
 aBox.setVal(33);
 Serial.print(F("aBox -> ")); Serial.println(aBox.getVal());
}

void loop() {}




all your empirically found knowledge is based on misconceptions or failure to get things done the right way. You should read a proper C++ tutorial.

Yes, compiler errors means there is something wrong. But making sense of the details can definitely help you understand what got wrong in the first place. (I reckon sometimes the compiler is cryptic)


Again, kudos and +1 karma for taking the time to jot that down, and expose your work here to the community - not always the most tender one for weird claims :) ... bare with us, constructive feedback and criticism is something positive.

I changed my constructor to Box1() and got this compiler error:
C:\Users\Owner\Documents\Rick\00Rick's hobbies\articles\software tools\Arduino\TestProgram\UsersCodeTry2\UsersCodeTry2.ino: In function 'void loop()':
UsersCodeTry2:16:6: error: request for member 'ChangeAnElement' in 'Box1', which is of non-class type 'Box1()'
Box1.ChangeAnElement(ABC, Ind, Val);
^~~~~~~~~~~~~~~
Using library Box in folder: C:\Users\Owner\Arduino\arduinosketchfolder\libraries\Box (legacy)
exit status 1
request for member 'ChangeAnElement' in 'Box1', which is of non-class type 'Box1()'

I changed the following lines:

in my .ino file:
//Box1 Box1(5);
Box1 Box1();

in my .h file:
//Box1(int pin);
Box1();

In my .cpp file:

//Box1::Box1(int pin)

Box1::Box1()

{

int pin = 5;

What did I do wrong?

Rick

What did I do wrong?

if the constructor has no parameter, in your .ino you instantiate without parenthesis, just

Box aBox;

ideally You should avoid having a variable with the same name as the class.

It kinda works because of something too complicated to explain here about struct having their own namespace and the compiler treating the conflict between variable name and class name by preferring the variable (unless instructed differently with the struct or class keyword)

please use code tags and don't quote my full post...

J-M-L:
there are definitely things to fix, but kudos for taking the time to share your notes.

here is an example of class names starting with non capital letters and all is working fine

class box {

public:
   uint8_t val;
   box(uint8_t v) : val(v) {}    // constructor
   uint8_t value() {return val;}
};

class cat {
 public:
   uint8_t val;
   cat(uint8_t v) : val(v) {}    // constructor
   uint8_t value() {return val;}
};

class dog {
 public:
   uint8_t val;
   dog(uint8_t v) : val(v) {}    // constructor
   uint8_t value() {return val;}
};

box aBox(10);
cat aCat(20);
dog aDog(30);

void setup() {
 Serial.begin(115200);
 Serial.print(F("aBox -> ")); Serial.println(aBox.value());
 Serial.print(F("aCat -> ")); Serial.println(aCat.value());
 Serial.print(F("aDog -> ")); Serial.println(aDog.value());
}

void loop() {}



it's just a convention to name the class starting with a capital letter. The language is case sensitive, so Box and box are two different things. 

here is an example of such a constructor with no argument


class Box
{
 public:
   uint8_t val;
   
   Box() {val = 0;}    // constructor
   void    setVal(uint8_t v) {val = v;}
   uint8_t getVal() {return val;}
};

Box aBox;

void setup() {
 Serial.begin(115200);
 aBox.setVal(33);
 Serial.print(F("aBox -> ")); Serial.println(aBox.getVal());
}

void loop() {}




here is an example of dynamic instantiation, within the setup with a global pointer 


class Box
{
 public:
   uint8_t val;
   
   Box() {val = 0;}    // constructor
   void    setVal(uint8_t v) {val = v;}
   uint8_t getVal() {return val;}
};

Box* aBox;

void setup() {
 Serial.begin(115200);
 aBox = new Box;
 aBox->setVal(33);
 Serial.print(F("aBox -> ")); Serial.println(aBox->getVal());
}

void loop() {}




or here is an example of just dynamic instantiation of a class instance that is local to the setup() function


class Box
{
 public:
   uint8_t val;
   
   Box() {val = 0;}    // constructor
   void    setVal(uint8_t v) {val = v;}
   uint8_t getVal() {return val;}
};

void setup() {
 Box aBox;
 Serial.begin(115200);
 aBox.setVal(33);
 Serial.print(F("aBox -> ")); Serial.println(aBox.getVal());
}

void loop() {}




all your empirically found knowledge is based on misconceptions or failure to get things done the right way. You should read a proper C++ tutorial.

Yes, compiler errors means there is something wrong. But making sense of the details can definitely help you understand what got wrong in the first place. (I reckon sometimes the compiler is cryptic)


Again, kudos and +1 karma for taking the time to jot that down, and expose your work here to the community - not always the most tender one for weird claims :) ... bare with us, constructive feedback and criticism is something positive.

I have used this strategy many times with good results. I clearly state what I think is right so experts can quickly spot what is wrong. As long as the comments are helpful, I don't mind a few barbs. My greatest fear is to disseminate wrong information.
Rick

PLEASE STOP QUOTING MY ENTIRE POSTS !! :slight_smile:

Robin2:
I was just using an example to bring to the OP's attention the fact that the way pointers are handled in C/C++ can be confusing even though the concept of pointers is straightforward.

...R

I keep rereading "Navigating C++ and Object-Oriented Design" by Anderson and Anderson. I'll continue until it sinks in but, so far, is not second nature.
Rick