This is a simple game I'm writing to test the idea of using games to teach C++. https://learncodethehardway.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

12 KiB

Stroustrup's PPP3 Study

The last time I did C++ seriously was 1996. That was 28 years ago. Damn I'm getting old. If I'm going to use C++ to make some games and teach people how to code C++ then I better relearn the latest stuff.

To relearn C++ I bought Stoustrup's Principles and Practice Using C++ 3rd Edition, or PPP3 for short. This part of the repository are my study files so people can read them, and my critique of the book as I go through it.

Getting the Code to Work

It uses meson so if you got the sfmldemo to work then you should be ready to go. You need to do this:

  1. mkdir builddir
  2. meson setup builddir
  3. meson compile -C builddir

The "exercises" are then compiled into builddir so to run ex06.cpp you do ./builddir/ex06.

Editing the Code

I'm using Vim with clangd and clang-tidy for checking the code while I work. I also use Tim Pope's vim-dispatch to run the builds but there's a slight problem with how Meson is setup.

Meson requires you to do the builds in builddir, and it does this by doing a cd builddir then running ninja. This keeps the source tree clean of build junk, but now all of the error messages from the C compiler are in the wrong location. They'll list an error in ex06.cpp as ../ex06.cpp. Vim can pick this up, then then it goes one directory up to find the file. It's incredibly irritating that Vim doesn't have an ability to adapt to this given how many C++ build tools use this pattern, but the way I solve this is simple:

  1. :cd builddir
  2. Run the build in Vim and now the error paths will be correct.
  3. Just remember to edit files with :e ../ex06.cpp.

This is dumb as hell but it's working for now. If you have a fancy way to tell Vim "Hey, all paths in the error buffer are one level up" then let me know.

Criticisms of PPP3

  1. Bad support code and future proofing. This book is attempting to teach C++23 (the 2023 version of the standard) in a world that doesn't correctly support 2023. To make this work he has these header files PPP.h and PPP_support.h and other random files you're supposed to include with no explanation on how to actually use them. When I try use them (they're included in this repo) they don't compile. I could spend the time to make them work, but it's actually easier to just take his code and make it work without these headers.
  2. Nothing actually works as written. Not a single example given in the book actually compiles. Header files are skipped even when they'rr essential to the code's function. He just assumes you'll use PPP.h but that doesn't even include everything correctly, and as I said, his files don't even work without a mythical advanced compiler from the future. Also, relying on PPP.h means people don't learn the real includes needed to make the code work.
  3. No full sample code available. Normally in this situation I'd go find the sample code download or see if there's a github repository of the code, but all of his links to sample code on the his website for the book are dead and sometimes labeled "TBD".
  4. Disjointed incomplete examples. There's all kinds of examples that feature code from one part of the system that relies on another part that either doesn't exist or isn't explained well. A good example is the calculator, where you're making a parser. In Chapter 5.6 he has code for parsing the calculator language that all use a Token_stream class, but only implements the Token_stream after all the parsing code. This means that none of the parsing code he's talking about actually works until you get to the Token_stream later, which is backwards. Many examples are also progressively improved versions of functions but that require later code to even work.
  5. No explanation on actually building the code or setting up an environment. In the very beginning he claims the book is for people who have no programming experience but want to work hard. He then dives right into the classic "hello world" but no explanation on how to get that to compile. No instructions for installing software, no explanation of build tools, or compilers that work, nothing. You might think this is to be "high class" and not focus on such "trivial nonsense" but there's only like 4 compilers he has to worry about, 2 if you want only C++23 features. He could totally figure out instructions for 2 compilers. Without instructions on how to properly setup and compile that first version the book is completely useless for the total beginner he claims to target. Even as a seasoned professional I couldn't figure out how to get his PPP.h to work, so how is someone who knows nothing about C++ supposed to figure it out? Since the code samples are incomplete, how is a beginner supposed to know they need #include <iostream> to make std::cout to work?
  6. Disorganized presentation of the core concepts. The book is all over the place. First you cover objects, types, and values, but this doesn't cover objects it covers base types like int, then it goes into computation but nothing about classes and struct, then he gets into error reporting with a reporting method that doesn't work without the PPP.h file you can't use, but then gets into writing a calculator that uses class but no explanation of that until Chapters 7 and 8 which finally explains all the things you needed to work on the calculator. It's understandable to not cover things until you can explain them later, since programming is so complicated you do have to gloss over concepts until later. But, Chapters 7 and 8 should have probably replaced the majority of the content in chapters 1-4 before he gets into making code with the concepts.

That's my critique for far. Basically, the book is your classic code book where the author has absolutely no idea who he's writing the book for, and just assumes that everyone who reads it has his brain and his computer. He most likely has a whole directory full of the code in the book but you and I will never see it. It's also definitely, DEFINITELY not a beginner book. I couldn't imagine someone who's never written code trying to setup MinGW with Meson on a Windows machine or even getting Visual C++ to work, let alone figure out his mishmash of PPP_*.h files. 6. Constantly using things that don't exist. The book is riddled with things that are claimed to be supported or standard, but that isn't available in many compilers. A great example is std::format, which would be great since I planned on having people use the excellent fmt library, but none of the compilers I have had this. He does this all the time where he just assumes something exists but it doesn't, and what's even more irritating about this is it's not hard at all to test. There's only like 4 compilers to verify. A single test suite could do it, but nope, I'm forced to research it myself.

The Weirdest Bug of 2024

I'm going to explain a super bizarre bug in the hopes that someone else runs into it and can explain to me what is going on. This is a compilation error on Windows that made no sense from the start, and then magically went away one day with no changes from me. If you get errors in libgit2/src/utils/process.h then see if it matches my description here.

I had the watchgit.cpp file working on OSX and decided to test the build on Windows. The only thing that failed on the build was libgit2/src/utils/process.h had several errors regarding missing types. To keep this description short here's the patch I crafted that solved the problem:

10a11,12
> typedef struct git_str git_str;
>
115,118c117
< GIT_INLINE(bool) git_process__is_cmdline_option(const char *str)
< {
< 	return (str && str[0] == '-');
< }
---
> #define git_process__is_cmdline_option(str) ((str) && (str[0]) == '-')

What's even stranger is that the compilation errors only showed up when I included any C++ headers. For example, here's code that I used to turn off/on headers to see if one was the culprit:

// #include <iostream>
// #include <iomanip>
// #include <fstream>
// #include <fmt/core.h>
// #include <regex>
// #include <iterator>
#include "libgit2-test.h"

int main(int argc, char *argv[])
{
  test_libgit2(argv[1]);
}

What's wile is do you see how I have a test_libgit2 function and I'm including a libgit2-test.h file? I even went so far as to place all of the libgit2 code into a separate compilation unit to firewall it off and the bug STILL showed up. That libgit2-test.h header had absolutely zero libgit2 headers or definitions. It only had the one test_libgit2 function in it.

Yet, even after firewalling the code off in its own .o file I would still get the compiler error if I uncomment any of the above C++ headers.

To summarize, here's the compilation bug so far:

  1. I receive errors in only src/utils/process.h in the libgit2 project.
  2. The errors are various complaints about struct git_str missing and GIT_INLINE(bool) macro not creating a proper type so the compiler complains that git_process__is_cmdline_option has an invalid type, and the usual string of bad syntax errors when syntax is missing.
  3. These errors show up any time a C++ header file like <iostream> or <string> is included.
  4. These errors even happen when you firewall the libgit2 code into a totally separate compilation unit, which should be impossible.
  5. These errors EVEN happen in MinGW 13 (released 2023) and clang 17, the latest ones I could get. The fact that the error happens in two wildly different compilers tells me it's a bug in the libgit2 code.
  6. Adding the typedef struct git_str git_str forward declaration and converting git_process__is_cmdline_option to a macro fixed the errors and allowed it to compile.

Thinking I'd mostly figured it out, and having a valid fix for the compiler error I start to work on a patch to submit to the libgit2 project. I spent an entire day just trying to figure out how meson wants it's patches before giving up. Life is too short.

After giving up, I was about to write a hack script that just copied the process.h over and then...it started working. At first I didn't believe it and though maybe I'd applied the patch successfully. I delete the patch and reverted the changes and...it compiled. One day later and suddenly it's as if the build is working and I did not do anything to fix it.

I suspected that maybe I had a strange environment or something about my machine. If it's suddenly working then I can't say it's a libgit2 problem. I did need to figure out what was causing it though and tried the following:

  1. For some idiotic reason meson loves to install random junk into Anaconda's directories. I think it looks for the first bin/lib/include directories in the PATH and uses those, and Anaconda's is first, so that's about the dumbest shit I've ever heard of doing if that's true.
  2. That made me think that maybe enabling an anaconda environment would cause it to fail but nope, works fine still. No weird errors when using C++.
  3. I thought it might be ccache related. Maybe ccache had a corrupted cache and it "magically" got cleared out this morning? If that's the cause then there's no way to replicate it other than keep coding and hope it comes back.
  4. Another thing was all of the errors pointed at the includes in the default MinGW install, even if I used clang, which is again so dumb. Why would clang pick up a random header file in the mingw install? But, again everything is fine now.
  5. I completely removed Anaconda and all previous files I'd installed from testing the build, rebooted, triple checking I had a completely clean setup, and it still was working.
  6. A final thing I suspected is maybe ccache was skipping recompiling? As in, somehow I managed to trick ccache into using the one working copy it made and didn't update? Just in case I ran ccache -C to completely clear it and it still compiles with no errors.

At this point I can't reproduce the compiler error I'd had for an entire week and this worries me. I'm positive other people will randomly run into this, so rather than continue trying to make it happen I'm going to continue development and wait for it to show up again.

If you read this and think you might know what is causing it, feel free to checkout the code and try to reproduce it. The failing build is in the PPP3 directory.