Showing posts with label book. Show all posts
Showing posts with label book. Show all posts

Thursday, 27 December 2018

C++ High Performance - Mistaken Statements

I am a huge fan of using the language to communicate my intent, especially when it comes to function parameters, I've talked about this before in terms of simple typing (by utilising "using" statements - to give meaning to simple/trivial types) and const correctness.


But being Christmas, I've had a C++ gift or two, and one of them is C++ High Performance by Victor Sehr and Bjorn Andrist, I've only spent a few minutes looking through this book, but one comment did jump out at me as odd... Not overtly wrong, just odd to me, maybe a different way to look at things...


No, I'm not talking about their constant calling back to compare with Java (rolls eyes) I'm talking about the two stanza's at the bottom:

"C++ Arguments passed as references indicates that null values are not allowed"

No, no that's not the point of references, the point is that you do not allocate memory for and copy the values from the passed resource into a local value for use within the function which will have a scope life-time of the function and therefore be de-allocated at the end of the function.  A reference, to me, has always primarily been a method of communicating copy semantics, we're referencing the remote value, if the parameter is not constant it maybe modified, thus:

#include <iostream>
#include <string>

int g_value(42);

void Change(int& p_Value)
{
p_Value += 1;
}

int main()
{
std::cout << "Before: " << g_value << "\r\n";
Change(g_value);

std::cout << "After: " << g_value << std::endl;
}

Or not modified, thus:

#include <iostream>
#include <string>

int g_value(42);

void Change(int& p_Value)
{
p_Value += 1;
}

void Print(const std::string& p_Pre, const int& p_Value)
{
std::cout << p_Pre << ": " << p_Value << "\r\n";
}

int main()
{
Print("Before", g_value);
Change(g_value);

Print("After", g_value);
}

If we were to allocate a value as a pointer we could send the reference to these same functions and they would be unaware of subtle problems (without modern runtime checks):

#include <iostream>
#include <string>

int g_value(42);

int *g_PointedToMemory(new int{ 100 });

void Change(int& p_Value)
{
p_Value += 1;
}

void Print(const std::string& p_Pre, const int& p_Value)
{
std::cout << p_Pre << ": " << p_Value << "\r\n";
}

int main()
{
Print("Before", g_value);
Change(g_value);
Print("After", g_value);


std::cout << "--------------\r\n";

Print("Pointed To Before", *g_PointedToMemory);
Change(*g_PointedToMemory);
Print("Pointed To After", *g_PointedToMemory);
}

With modern runtime checks this code would still compile, you are not "defending" from null pointers by using the reference syntax, you're simply stating not to take a separate copy of the thing being passed in.  However, if you dereferenced a null pointer (nullptr) int* as the parameter being passed, the code would still compile, but you would receive a runtime access violation, as you're trying to write to nullptr.

#include <iostream>
#include <string>

int g_value(42);

int *g_PointedToMemory(new int{ 100 });

int *g_Unallocated(nullptr);

void Change(int& p_Value)
{
p_Value += 1;
}

void Print(const std::string& p_Pre, const int& p_Value)
{
std::cout << p_Pre << ": " << p_Value << "\r\n";
}

int main()
{
Print("Before", g_value);
Change(g_value);
Print("After", g_value);

std::cout << "--------------\r\n";

Print("Pointed To Before", *g_PointedToMemory);
Change(*g_PointedToMemory);
Print("Pointed To After", *g_PointedToMemory);

std::cout << "--------------\r\n";

Print("Unallocated Before", *g_Unallocated);
Change(*g_PointedToMemory);
Print("Unallocated After", *g_Unallocated);

}

This code is clearly dereferencing an unallocated piece of memory, it builds no problem... This is where my mind skews on the comment in this text, it's a throw away line, but so miss leading to my eyes.


But this code will not run:


This is where I totally take issue with the statement in the text.

Getting this kind of runtime error, because we've followed the advice of this text, we may move onto the next line of the advice:

"C++ arguments passed as pointers indicates that null values are being handled"... EREEEEERGGGGH.  No it does not, lets look at that, lets follow Alice down this rabbit hole with a new function, to take our unallocated integer pointer and try to dereference and print it... The text tells us "null" are being handled, we'll be fine, let us update our code:

#include <iostream>
#include <string>

int g_value(42);

int *g_PointedToMemory(new int{ 100 });

int *g_Unallocated(nullptr);

void Change(int& p_Value)
{
p_Value += 1;
}

void Print(const std::string& p_Pre, const int& p_Value)
{
std::cout << p_Pre << ": " << p_Value << "\r\n";
}

void PrintPtr(const std::string& p_Pre, const int* p_Value)
{
std::cout << p_Pre << ": " << *p_Value << "\r\n";
}

int main()
{
Print("Before", g_value);
Change(g_value);
Print("After", g_value);

std::cout << "--------------\r\n";

Print("Pointed To Before", *g_PointedToMemory);
Change(*g_PointedToMemory);
Print("Pointed To After", *g_PointedToMemory);

std::cout << "--------------\r\n";

// The alternative Print
PrintPtr("Unallocated Before as Pointer", g_Unallocated);

std::cout << "--------------\r\n";

Print("Unallocated Before", *g_Unallocated);
Change(*g_PointedToMemory);
Print("Unallocated After", *g_Unallocated);

}

And now lets run the program again...

Yes, it explodes, with the exact same runtime error... yet the text said "null values are being handled".  And it is just totally untrue, the truth is that pointers like this simply mean you can pass something which points to the memory you want to use (passing a pointer is like passing a small number of bytes - depending on your architecture - rather than passing a reference to the memory a pointer maybe anywhere in the addressable memory space, and it does not have to necessarily be a pointer from "new" or "make_shared" (etc) it can be any memory...

So, we could make this PrintPtr function print our allocated "g_value" from the earlier pieces of code, by making the call with a dereferencing:

I am going to plug on with this book, I hope to find another hour tomorrow to start going through their advice for speeding up modern C++.  Unfortunately, right now, I am surprise and somewhat dismayed with this miss-leading stance, and I have three other pages folded down to follow up on this exact theme, the text taking you slightly off of course or making statements which are not correct C++ nor correct thinking.

Wednesday, 12 September 2018

Form of Ready Player One Sequel?

I've just finished a re-read of "Ready Player One"; and I heard tell that Ernie had embarked on writing a sequel... Now before you all start I make the distinction between the film and the book, we are talking about the book.

In fact, those of you who haven't read the book... (Spoiler alert)... It starts out being written as the true story, to set the record straight... Straight up calling the movie out as wrong!  Oh yeah, love that preemptive strike on the Spielberg saga.

Anyway, the sequel eh?... Lets gather around the camp fire and think about this...

It can't be another hunt, we've had a hunt, Z is the man now, we may get a little friction between the members of the high-5 and Z maybe?... But it's going to be about what they find within the folds of the fabric of the Oasis.

Perhaps whether Halliday's Avatar was some form of personality transplant from his real-world body into the Oasis, some form of literal Ghost in the Machine?

I'd like to see some form of digital court, like a war-crimes tribunal, all accounts whom were Sixers being required to justify their existence?  Maybe some form of play on "The Odessa Files" in digital form, with IOI has the continuing evil?


Maybe there's some form of massive community lash back, everyone died on the release of the Cataclysm, Z resurrected the High-5 members, but everyone else was perma-death?



Saturday, 9 December 2017

Start C++?

I've been writing C++11 or better code, well since 2010, as I started doing so with the TR1 as was.  We're nearing eight years since then, and it starts to show.

So, where would I recommend starting to learn modern C++?  Well, if you've never programmed before, don't start by learning C or C++, go learn Pascal, or Python, or something else which is more friendly.  I started out in Pascal, for a good three years, before I started to work in C and later moved into C++ (in 1998) so if 20 years of C++ teach me anything, it's don't try to learn if as your first language.

Once you have a concept of how to program, then start to learn C++, and I would recommend finding someone - hopefully like me - and asking them.  An hours chat with them, to help swap what you know with what they know, is a good start.

Saving any such friends, YouTube, watch CPPCon, BoostCon, watch talks about programming C++ but most importantly get a development environment and cut some code, if you must go for a community edition of Visual Studio 2017, but otherwise get a Linux machine up and running and have a play.

If you work in an office where there's a C++ guru, ask them, if they're worth their salt they'll be more than happy to take you through a few things.

And failing that, I include a set of programs below, one through three, these are the most simplistic C++ programs I would recommend you start off working with, and if you want to know more comment below.


So, open an editor, and write this C++ code, then save the file as "main.cpp":

#include <iostream>

int main ()
{
   std::cout << "Hello World";

}


If you are in Visual Studio, you run that file, if you're in linux close the editor after saving and lets use the gnu g++ compiler (install it in Ubuntu say with "sudo apt install g++"), and you compile this into a program like so:

g++ main.cpp -o example1

Your program output will be called "example1", and you can run that program, and it'll say "Hello World".

The code itself I want to only explain the first line... the include, this tells the compiler to include some code for you to use, and "iostream" is the input/output streaming functions for you.

int main is the first function the program starts to run from, int is the return type, meaning integer, but in C++ we don't need to return a value here - if anyone argues with you about this, they're wrong.  The name "main" is the a special name and the compiler makes sure your program starts with this function entry point.  The empty brackets show we're not passing anything into the function, there are no parameters.

The braces mark out the body of code for the main function, so it starts with an open brace, contains lines of code and then ends with a close brace, and yes I call them "braces" not "curly brackets" :)

The one and only line of code we've got left is the output call, this is streaming the value on the right of the "<<" chevrons to the standard character output stream... "std::cout"... I'll reiterate 'standard character output stream".   And the value we're streaming is a string (note the quotes in the code) "Hello World".

This is the only line of code with the all important semicolon ending, this is used to tell the compiler that we're done with out line of code.

Go, try this...


The next most basic program for C++ noobies to learn, is to maybe output some more kinds of data.... After say, asking the user something...We've seen cout, how about "standard character input"?...

#include <iostream>
#include <string>

int main ()
{
    std::cout << "What is your name? ";
    std::string name;
    std::cin >> name;
    std::cout << "\r\n";
    std::cout << "Oh Hi " << name;
}

We're introducing a new header, the "string" header helps us store strings of characters in the type "standard string"... "std::string"... Did you spot that?... This is our first variable, and it has the name "name" so we can refer to it later.

As ask the user a question by sending characters out "<<" to the output stream, and then we read them back in ">>" from the character input stream, and we read them into the "name" variable we just created.

Next we output a carriage return "\r" to move our carat back to the left of our screen, and we move to the next line "\n".

And finally we output a piece of text AND our input variable!

You can play with this code with other variable types, other than string... Try "int" to read in a whole number, of "float" to read in a floating point number.


But our third program, will involve some actual processing.... Lets average the age of a set of people we ask the user to input...

#include <iostream>

int main ()
{
    int TotalAge (0);

    int age;
    std::cout << "Enter the name of person 1: ";

    std::cout >> age;
    TotalAge = TotalAge + age;
    std::cout << "\r\n";

    std::cout << "Enter the name of person 2: ";
    std::cout >> age;
    TotalAge = TotalAge + age;
    std::cout << "\r\n";
    
    std::cout << "Enter the name of person 3: ";
    std::cout >> age;
    TotalAge = TotalAge + age;
    std::cout << "\r\n";


    float Result = TotalAge / 3;

    std::cout << "The average Age is: " << Result
}

Here we have much more code, we ask for each persons age, adding it to the total, then we calculate the resulting average age and print it out.  Take a moment to look at this....

What do you see?  If the first thing you see is that there are three sets of the same code in there, then you have the seed of a programming within.  If however you just see a mess, then maybe C++ isn't for you.