C++ - Teaching old dog new tricks?

Status
Not open for further replies.

KurtE

Senior Member+
Was wondering if there are suggestions on a good reference to teach old dogs like myself the new tricks of C++ ;) :D

I personally learned C in the 1980s and C++ probably in the early 90s and with C++ the simple rule of thumb was to avoid most of the complicated stuff otherwise things have a tendency to bloat quickly...

So recently been seeing a lot more stuff geared around templates and the like and discussion talking about:
Can we switch to 'gnu++14' verses use Can we switch to 'gnu++11'

And figured maybe I should try to catch up some!

Suggestions?
 
Was wondering if there are suggestions on a good reference to teach old dogs like myself the new tricks of C++ ;) :D

I personally learned C in the 1980s and C++ probably in the early 90s and with C++ the simple rule of thumb was to avoid most of the complicated stuff otherwise things have a tendency to bloat quickly...

So recently been seeing a lot more stuff geared around templates and the like and discussion talking about:
Can we switch to 'gnu++14' verses use Can we switch to 'gnu++11'

And figured maybe I should try to catch up some!

Suggestions?

Funny,
I just put away my Stroustrup book on C++ to read about the template stuff (triggered also by tni's low-latency-logger example)
Not that I wanted to program generic classes but at least I wanted be able to read this stuff.

I'm also interested to read more about it.
 
Last edited:
+1. TNI makes good use of features that show low overhead and great utility but wholly foreign to me.
 
Funny,
I just put away my Stroustrup book on C++ to read about the template stuff (triggered also by tni's low-latency-logger example)
Not that I wanted to program generic classes but at least I wanted be able to read this stuff.

I'm also interested to read more about it.
Exactly! Also Paul's latest change for the MAP function!

Also the suggestion that one might be able to use a template class to maybe have void callback(void) like functions, the ability to use a template to be able to pass a this pointer to...

So ordered the recent Stroustrup book... Only goes to ++11 but it is probably a help!
 
Whatever you do, don't read anything that doesn't at least cover C+11. The language and coding style has changed quite a bit for the better...

Well, I got my version in 1994 (reprint of 2nd edition) at a time I only programmed in C++ on DEC Alpha.
It seems to be as with smart pointers: All memory is released when objects get out of scope.
Anyhow, I will follow the literature pointers
 
Hi all. I finally got my copy of Stroustrup book and got to say its pretty good. Easy to understand so far but only got through a few chapters. Yes I am starting from beginning. One thing is that all the books make use of cin and cout. As PS pointed out Arduino never got implemented or got it working. So I got curious (yes I am a gluten for punishment) so I started to see if any of the Arduino boards have it working. None of the Arduino boards(Mega, 101, uno) I tried had cout working, as expected, but it did work for Intel Edison, Galileo, and Onehorse's ESP32. So I did a little digging both are the same.

Anyone know what would happen if I would move one version to the other?

Thanks
Mike

UPDATE: I digging into it and has something to do with templates, I think. Haven't got up to that chapter yet. The base_ostream is in a ostream.tcc file in the bits directory which seems to be the problem (found that out by including it in <ios>

Update 2: Was going back over some stuff I was working on quite some time ago and remembered I had this working with the standardcplusplus for avr (ref http://www.gammon.com.au/forum/?id=11119) and before I just started using Mikal Hart's Streaming library. It looks like they had created a wrapper for ostream to a serialStream class that took care of interfacing. Don't think I wan to mess with this right now.
 
Last edited:
As I mentioned I also purchased the Stroustrup book and have made it through several of the chapters and learning some new stuff...

Things like using: using instead where I would have used typedef before...
Read through into of templates and the like, may continue skimming through chapters in order...

Makes sense that for example Arduino Edison it cin/cout would work. Their Arduino simply builds a linux app, which gets installed at a known location, which runs at boot (if configured to do so).
 
KurtE - you got through a few more chapters than me. I went through the other book I referenced, not bad learnt of things about all the errors I got over the years but the S. book really starts from scratch and a lot more in depth. The last C book I read was by Richie. Still have it.
 
Last edited:
;)- I think I still have the K & R book around here someplace.

Have a few other C books as well, like Embedded C programming for Atmel AVR by Barnett (2 editions), C programming for Microcontrollers, and probably a few others...

First Computer Science books I still have are three books "Art of computer programming" by Knuth... But those were from a long long long time ago.
 
Which 'the Stroustrup book' titles and versions? I just bought a couple I think and they are still being used to sample dust collection rates. In fact it seems I lifted something shortly after getting the last ones - only to see I already had one of the same title that had graduated from dust sampling to a spine warping test.
 
@KurtE. Memory lane. :)

@defragster. I picked up the Programming: Principles and Practice Using C++ (2nd Edition) which covers is c++ 11 and 14. May eventually get the 4th edition of his C++ programming book, we'll see.
 
Thanks - almost a year back I got 'The C++ Programming Language (hardcover) (4th Edition) ' - my 2012 copy of 'Programming: Principles and Practice Using C++ ' is version 1 not 2 edition (May 25, 2014)
 
Update: I am slowly making it through the Strousrup, 4th edition book. Which as I mentioned earlier. But as I read through the book, I keep asking myself how much of this currently applies to the Arduino Environment and likewise how much should apply.

The good thing about doing this exercise is it will help me get back into better learning and using ROS.

Especially since the Arduino/Teensy world grew from chips that ran at 16mhz with limited ROM/RAM to our newer processors that can run over 200mhz that have comparatively lots of memory and program space... Also Arduino world went from simply blinking an LED or two and maybe checking a button or two, to lots of capabilities...

Things like:

Exception handling: I started a different thread on this, but should a standardized methodology be setup to report and recover from errors? For example should we go to try/catch?

Memory allocation/usage: There are those who believe you should avoid dynamic memory allocations at all cost... There are those on the opposite end who believe everything should be dynamic... I am somewhere in between.

Example: Currently buffers for things like Serial ports is hard coded in the different classes. To make a larger buffer, requires you to edit the sources and then all programs get the updated size...
Would be great to make user settable. One option is, have the user define their own array and call a method to set the buffer to their new buffer. Another approach is have the new method, where you simply tell it the size you want and the system allocates it... Both have pluses and minus. Both can easily have the user screw up. Like call asking for 32K buffer and maybe I don't have 32K and corrupts stack... Or with pass in buffer, suppose I call this with buffer on stack, or I pass the same buffer to multiple objects, or ...

Resource scoping: Again assuming you actually use things like NEW, should the code be converted to use unique_ptr/shared_ptr...

Using STD libraries - How much of this to use? Should I convert my simple arrays to std::vector? Should we rework all of the system to get rid of all of our own queue code and instead use std::queue. Should I/we convert from using char* to using std::string?

But again I think a lot of this also boils back up to memory allocation and error recovery.

Lots to think about
 
For almost 15 years, some of the direction for ISO C++ has been from the IEC Technical Reports. One of the original attempts to make memory more safe was from the Boost libs - the 'enhanced member pointer adaptor' and the wrapper for the fixed-size array.

The pro forma 'standard' for embedded systems, at least in my neck of the woods (desert?) is the MISRA C check, where anything associated with a malloc is a failure. There is much stuff in the MISRA C lists that talk about the std libs. So worth a read.

If the box will be shipped to a customer, nothing goes out the door with a dynamic allocation from the heap, and the stack size is set up once at power-on/reset. For internal stuff, allocate everything one at start-up, then nothing more dynamic. The major weakness for systems that will run for months or years is monitoring usage and re-use of this pre-allocated stuff, so just no longer do stuff with shared or re-allocated space.
 
Example: Currently buffers for things like Serial ports is hard coded in the different classes.

Martino did some work on this a couple weeks ago. There's still no indication whether or not it'll ever officially merge into Arduino's API... but I'm feeling like this is probably the way forward for Teensy. It does add extra complexity and overhead in the serial code...

https://github.com/facchinm/ArduinoCore-samd/commits/RingBuffer_extra_rebased

https://github.com/facchinm/ArduinoCore-samd/commit/e3f2d8785f1344cfb655cd4d138bcd4261d13cf2
 
Hi Paul, I do like the idea of using a common ring buffer class, like the current SAM/SAMD implementations do. And it would be great to go through and do it.

I also like the idea of being able to control the size of the RX/TX buffers. I am a little on the fence if I like the addStorage way of doing it. I personally prefer keeping it sort of simple as have methods like setRXSize(), setTXSize() or maybe members on begin for size and have a system function allocate the buffers at the first time begin is called...

I like this approach as you don't waste memory for devices you don't use, and I personally don't like the idea of simple user screw ups killing things...

However I know I am maybe in the minority as I know there are those who want a statically defined system where there are no malloc or new or... And the user could screw up and says set my rxBuffer to 32K when I only have 8K free... Or more subtle where they allocate enough where their stack later collides with the heap...

But I can also easily imagine user screw ups using the add storage method as well. Users could do things like:
Code:
void setup () {
    char * extra_buffer[128];
    Serial1.begin(...,RXBUFFER(extra_buffer));
The buffer is on the stack, and...
or
Code:
char * extra_buffer[128];
void setup () {
    Serial1.begin(...,RXBUFFER(extra_buffer), TXBUFFER(extra_buffer));
Use same buffer for TX and RX... Likewise, I can easily imagine something like:
Code:
char * serial1_buffer[128];
char * serial2_buffer[128];
void setup () {
    Serial1.begin(...,RXBUFFER(serial1_buffer));
    Serial2.begin(...,RXBUFFER(serial1_buffer));
They did copy paste for Serial1 to Serial2, but did not update which buffer and so both use the same extra...

As you mentioned adding the extra buffer adds a little complexity to the code. Like storing a character:
Code:
void RingBuffer::store_char( uint8_t c )
{
  int i = nextIndex(_iHead);

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if ( i != _iTail )
  {
    if (_iHead < size) {
      _aucBuffer[_iHead] = c ;
    } else {
      additionalBuffer[_iHead - size] = c;
    }
    _iHead = i ;
  }
}

Again probably not a big deal, it simply has to check if an index is in the fixed part or the additional part...

I see that his implementation has variables like _iHead as variable type int, could imagine it could be something like uint16_t, but that would restrict the max size.

So I like the idea of the flexibility going either way... For those who wish to not waste memory on devices they don't use, then maybe they could setup to have all of the devices allocate a very small buffer like 8 bytes... And then for the ones they use they allocate the extra..

Edit: As there is already a Pull request into main Arduino probably should just do it!

Let me know if you want to do it, and if you want any help
 
Last edited:
@KurtE. As a normal user I am assuming that if you don't specify any buffer sizer it will default to a standard size. Then if you so desire, and know that you are doning you have the option of setting up the buffer sizes.

As an aside to some of you issue I think you probably could set up some checks and balances to avoid worse case scenarios. Almost set up a min/max buffer size depending on the processor you are using.
 
@mjs513 - With the proposed way in the PR Paul linked to, You still have the default buffer that is created. So the new optional parameters to calls like Serial1.begin(, is new buffers to add on to the system provided buffer.

With the alternative version I mentioned, that would do a malloc or like of buffer, yes they would all have an initial size for the buffers specified. But what I suggested was you only allocate any buffer if the user actually uses the device... And yes you could have the APIS, so some validation/sanity tests to try to keep from having the user screw them selves.

But again Many options - So I say choose one and go with it!
 
Always better to try and pick the best one and go for it. Tried going through changes in the links but had a headace was up way too late last night. :) Reason I was asking is that I know you are going to get someone like me trying to change the buffer size and not really understanding what they are doing and mess things up. Just like you said.
 
Last edited:
Status
Not open for further replies.
Back
Top