Learning C++

This is a series of things that I found out when learning C++. The approach is going to non-standard and likely to annoy any normal C++ professional. I am interested in languages in general and so I will be doing all my early code without and STL at all. I want to learn what features the language offers before starting to use the standard libraries. To this end I try to do do simple version of the features that are already available. That way I can see how these features are implemented in the standard libraries. To me that feels like a important advantage in learning the language. I did try to read the code for the libraries but it has a lot of very nuanced code that I will only get to understand much later on. The code is still interesting to step through however as it does offer clues as to how to unpick various implementations.

Coding Environment

I will be using Visual Studio for the editor, compiler and debugger. I did try Visual Studio Code for a while including using it to look at Rust. However I find that while VS is bloated and has bugs etc, it is has the easiest entry into starting to code.

I was going to picking C++ 11 as the baseline, but VS starts with 14 as a default so I will go with that.

I have found one thing from C++ 17 that I would like which is the “if constexpr” as this allows for compile time code removal. Without this in the example below, the compiler needs to be told to ignore any warnings from the other code path.

    if constexpr ( is_same_v<key_t, const char*> )
    {
        hashable = (uint32_t)key[0];
    }
    else
    {
        hashable = (uint32_t)key;
    }

Learning by stepping through the STL implementation

Visual Studio STL implementation can be found here:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC

So you can go there to read the code. It is also possible to step through the STL code. Note that Visual Studio setting default to always stepping over this code. To change this go to the menu Debug -> Settings and turn off the setting Enable Just My Code.

This can be very instructive in seeing how these things are designed. An example is that I saw that unordered_map with the std::string will take a deep copy of the chars when it is storing them in the key an values pairs. I also go to see the type of allocators that are used by default.

Be warned, as the code looks complex as it has all the support for all the compiler versions. This makes the code look very cluttered and hard to read but focusing on where the code goes can help with understanding it.

The IDE settings have grown over this process, so I have a full page dedicated to the setting from the default. See:

Speed of Learning

This methodology, does mean I have to go very slowly, trying to understand each part of an implementation. I am not that quick a coder in the first place so this is a long process. I don’t mind as I am enjoying the journey. I am using the Internet as my manual and user guide and am very happy to find the answers out there rather than just randomly trying solutions. Do this, however, has the effect that sometimes something I learn does not stick. This is also one of the motivations to doing these posts. It will allow me to capture the coding patterns rather than having to commit them to memory.

Annoying Stack Overflow

I have a number of interesting web sites that help this journey (see below), but obviously Stack Overflow is an important resource. Interestingly, as I learn I find that some answers out there, while correct, may not be what you are looking for. So it is important to keep in mind your real use case when reading Stack Overflow. There is also a lot of historical answers out the and not only has the language moved on to provide better solutions but also the accepted thinking has moved on as well.

I did try asking a question and found that they seem to want code that does not work so they can show how to fix it. I put three solutions to a problem to ask which of them was the preferred idiom in C++ and was told that working code should be submitted on Stack Exchange. So I posted there as well and was told to put the code on Stack Overflow. Humm.

I did get some very good answers and a lot of patience from one particular contributor, so I owe a lot to them – thanks. I jump in and did not know the acceptable protocol when submitting there.

Here is a list of things I found from posting just two questions:

  • Try to post a very small thing with a very specific question. And with broken code if possible.
  • Don’t expect them to reply with a solution that is just in C++. If an STL function will do the job they will consider that the answer. This is hard if you have a bigger problem that is hard to reduce down to a small example that is then not trivial enough to be solved with one library call.
  • The comments section is very limited and not really to be used for chat or interactive discussion.
  • There is a chat system but you have to have enough points to use it. These are gained by up votes.
  • If you post a question they don’t like or your have not explained it well enough it will be down voted and you lose points.
  • They have an expected style guide that you should conform to. If you don’t then the answers will all be about fixing that. I would suggest K&R brackets and one of the standard variable formats (m_member, first_entry etc).
    • Don’t use _variable_name as this this had a lot of discussion already and the outcome is to not use it anywhere even if it is legal.

In retrospect it is a bit obvious that I should have read the tour and read the FAQ.

Bit Fields

So what is wrong with the 8 byte packed structure below?

    struct meta
    {
        char            twoCC[2];
        bool            isStack : 1;
        bool            isPowerOf2 : 1;
        bool            isBlocks : 1;
        bool            isStatic : 1;
        bool            isFreed : 1;
        int             padding : 3; 
        size_t          twoByteIndex : 16;
        uint8_t         powerOf2Index;
        uint16_t        size;
    };

Times Up!

Changing type in the list will trigger an alignment.

While there is support added for bool in C++ other ‘integer types’ are not supported, and you certainly cant go over 8 bits. I liked the idea of keeping the type, so the code that used it would be OK when assigning, and having the size be smaller than its default, but that is a step to far.

 struct meta
    {
        char            twoCC[2];
        bool            isStack : 1;
        bool            isPowerOf2 : 1;
        bool            isBlocks : 1;
        bool            isStatic : 1;
        bool            isFreed : 1;
        bool            padding1 : 1; 
        bool            padding2 : 1; 
        bool            padding3 : 1; 
        uint16_t        twoBytIndex;
        uint8_t         powerOf2Index;
        uint16_t        size;
    };

Pre and Post Increment and the operator++

There is a mantra that is ‘learn to do pre-increment because it is better’. Well that is hard for those that were taught in the old days when this was not a thing. I finally heard a good explanation why… This is about performance and what is returned from the operator. All C statements return a value, even if it is not used.

int i  = 1;
i++;

The second line will evaluate to 2, but then be thrown away.

int i  = 1;
++i;

This second line will return the original value of i (the value 1) but internally do the work to increment the value of i. This would not normally be an issue as the compiler will optimise any extra work away. But! What about a class where the increment operator has to be either the pre or post increment type. For the post increment the code will have to return a new value and that could incur the cost of the ctor and copy. Where as the pre increment can return the original value/class and hence have no cost.

Hence the rule of ‘do pre increment’ as a general rule is good as you can get into the habit of it.

There is an extra part to this that needs some care too. Example taken from Visual Studio Documentation

// Prefix increment operator.
Point& Point::operator++()
{
   _x++;
   _y++;
   return *this; // Compiler can optimise
}

// Postfix increment operator.
// int is required but ignored.
Point Point::operator++(int)
{
   Point temp = *this;
   ++*this;
   return temp; // May create a copy it can't optimise
}

Links