Showing posts with label principles and practice. Show all posts
Showing posts with label principles and practice. Show all posts

Thursday, 2 November 2023

Chapter 27 // Exercise 16 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022 and ISO C11 Standard.

Chapter 27 // Exercise 16

Use macros to obscure (simplify the notation for) the implementation in the previous exercise.


Of course the final exercise had to be creating some disgusting macros. And with that


I can't believe that was the final exercise. It's been 7 years. I bought this book back in January 2016 with no knowledge of programming (apart from some brief HTML back in '98) and now I've been programming in C++ for 7 years, 4 and half of them professionally; I get paid to make video games, it's crazy.

I'm going to spend a bit of time collecting my thoughts and then come back with a full review of the book. I think my title is going to be "Why you should learn C++ as your first language". I honestly don't think I would've wanted to learn C++ any other way now.

I've been feeling a bit nostalgic lately and going back over books I bought way back in 2016 and 2017 as well as reading my green text on the early exercises of P&P posted on here. I read a chapter 4 post and the memories of that night came flooding back to me. It took me 6 hours to write a function comparing the size of two ints. It was 3am and I was so frustrated I started crying. I developed a fear of bools. Its absolutely hilarious to me now as I sit here reading a random book, silently ripping apart the authors code because they stored an pointer to an enum.

My mantra at work is "git gud". I can relax a bit when I've "gitten gud", then I can "git gudder". I honestly believed I hadn't really changed at all but lately I've started to realise that I can "just do stuff now". 6 years ago I bought Programming Game AI by Example by Mat Buckland. I tried to read it and follow along with the code but it went completely over my head as I had no experience with inheritance and I was convinced OOP was evil (because I didn't want to understand it). Recently, I picked the book back up and not only implemented the code from snippets with no problem but changed it as I was going along, grumbling about the authors use of singletons and lack of interfaces.

2 years ago I read the online book Ray Tracing in one Weekend and attempted to multi-thread it. It was a disaster and no matter how many times I read MSDN or StackOverflow I just couldn't get my attempts to work. Earlier this year I had another go and got a hacky version working in an afternoon. It's not perfect but it worked and I just "knew" how to do it.

3 months after starting as an intern at Rare, I asked a senior in a catch-up "when do you stop feeling like shit?" and his response was "you don't, but over time it won't be as bad". I pressed him for an actual time for "over time" and he just laughed and said "3 years". It's been 4 years since I asked him that.

It took me a very long time to stop feeling like a waitress/receptionist and start thinking of myself as a programmer. 

Thank you Bjarne for giving me the tools to change my life. 

Wednesday, 1 November 2023

Chapter 27 // Exercise 15 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022 and ISO C11 Standard.

Chapter 27 // Exercise 15

Simulate single inheritance in C. let each "base class" contain a pointer to an array of pointers to functions (to simulate virtual functions as freestanding functions taking a pointer to a "base class" object as their first argument); see section 27.2.3. Implement "derivation" by making the "base class" the type of the first member of the derived class. For each class, initialise the array of "virtual functions" appropriately. To test the ideas, implement a version of "the old shape example" with the base and derived draw() just printing out the name of their class. Use only language features and library facilities available in standard C.

Jesus christ, I had to get ChatGPT in for this one because I had no idea how start. What a load of faffing about. I used to think inheritance in C++ was confusing, well get a load of inheritance in C! It all kind of seems a bit pointless in C. 

I didn't go too far into this exercise, literally just creating the draw() functions for a shape and a circle. Shape can also be created, it's not abstract.

Tuesday, 31 October 2023

Chapter 27 // Exercise 14 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022 and ISO C11 Standard.

Chapter 27 // Exercise 14

Write a function that takes an array of ints as its input and finds the smallest and the largest elements. It should also compute the median and mean. Use a struct holding the results as the return value.

Ah finally, a nicer "easy" one. This reminded me a lot of the exercises we did way back in chapter 4. God, that feels like a lifetime ago.

Monday, 30 October 2023

Chapter 27 // Exercise 13 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022 and ISO C11 Standard.

Chapter 27 // Exercise 13

Write a program that does the equivalent of strings; cin >> s; in C; that is, define an input operation that reads an arbitrarily long sequence of whitespace-terminated characters into a zero-terminated array of chars.

I was a bit confused on this one as cin stop reading in characters when it encounters whitespace; have to use getline() to include the whitespace. So with the statement "define an input operation that reads an arbitrarily long sequence of whitespace-terminated characters" I decided instead he meant more of a getline() so we include whitespace characters. So if you were to type "this is a sentence", it captures the entire sentence and prints "this is a sentence". Maybe that's not what he meant though.

Sunday, 29 October 2023

Chapter 27 // Exercise 12 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022 and ISO C11 Standard.

Chapter 27 // Exercise 12

Implement a (C-Style string, int) lookup table with operations such as find(struct table*, const char*), insert(struct table*, const char*, int) and remove(struct table*, const char*). The representation of the table could be and array of a struct pair or pair of arrays (const char*[] and int*); you choose. Also choose return types for your functions. Document your design decisions.

I originally did a really simple hash function however ended up with collision in the table quickly. I did some searching around and came across the terms "linear probing" where we keep going through slots to find a new one, wrapping round to beginning if needed.

The implementation isn't perfect but it'll do. For my design decisions:

Representation: I chose an array of struct pairs because it keeps the key-value pairs together, which makes it more intuitive.

Return Types: For find(), I return the int value associated with the key. If the key isn't found, I' return a special value, like -1.
For insert(), I return 0 if the insertion was successful and -1 if not (for example, if the table is full).
For remove(), I return 0 if the key was successfully removed and -1 if the key was not found.

Handling Collisions: For simplicity, I handled simple collisions only. This means the table has a fixed size, and if it's full, you can't insert more items.

Capacity: The table has a fixed size, which for this example, is set to 100. it will wrap round to find free slots but after that it won't insert any more.

Saturday, 28 October 2023

Chapter 27 // Exercise 10, 11 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022.

Chapter 27 // Exercise 10

Make a list of C language features adopted from C++ or C with Classes (section 27.1).

Github: N/A

  1. Inline Functions: Initially a C++ thing, it lets you suggest to the compiler that a function should be inlined for performance.
  2. Single-line Comments: I believe // was first used in B, then brought back in C++ because /**/ is tedious.
  3. Variable Declaration Anywhere: Instead of only at the beginning of blocks, you can now declare variables anywhere in C, much like in C++.
  4. Mixed Declarations and Code: Kinda related to the previous one. You can intermix variable declarations with executable code.
  5. Boolean Type (_Bool or bool with stdbool.h): I was shocked the first time I tried to use a bool in C and found out it had to be added to the language in C89.
  6. Compound Literals: Lets you create temporary structures or arrays in the middle of an expression, similar to how you'd do in C++.
  7. Designated Initializers: A more expressive way to initialize struct or array members.
  8. Static Assertions (_Static_assert): Helps catch errors at compile-time based on constant expressions.

Chapter 27 // Exercise 11

Make a list of C language features not adopted by C++.

Github: N/A

Had to look these ones up. It was actually very interesting.
  1. Complex Number Support (_Complex and _Imaginary): C has built-in support for complex numbers, while C++ relies on the std::complex template class.
  2. Different Treatment of inline Functions: In C, an inline function's definition can be present in multiple translation units. In C++, inline functions have external linkage by default, but in C they have internal linkage.
  3. Allowing Implicit Function Declarations: Older versions of C allowed functions to be called without a previous declaration (not recommended, though!). C++ never allowed this.
  4. Named Address Spaces: Some C compilers for specific platforms (like embedded systems) support this feature, but it's not a part of C++.
  5. Non-constant Aggregate Initializers: In C, global and static aggregates can be initialized with non-constant values. C++ doesn't allow this.

Friday, 27 October 2023

Chapter 27 // Exercise 9 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022.

Chapter 27 // Exercise 9

Using only C facilities, including the C standard library, read a sequence of words from stdin and write them to stdout in lexicographical order. Hint: The C sort function is called qsort(); look it up somewhere. Alternatively, insert the words into an ordered list as you read them. There is no C standard library list.

I could not get EOF to work in the slightest with scanf and ctrl+z. I decided to go with the qsort approach because no thank you to the list, so you have to enter the max amount of words to get it to quite the loop. Sometimes it would register the EOF, sometimes it wouldn't, sometimes I had to spam it on the keyboard and it would eventually register it.

Thursday, 26 October 2023

Chapter 27 // Exercise 8 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022.

Chapter 27 // Exercise 8

What is the lexicographical order on your machine? Write out every character on your keyboard together with its integer value; then, write the characters out in the order determined by their integer value.

Ah finally, a nice "easy" one. Although I kind of cheated. I basically just looped through the ascii table from 0 to 255 and printed everything. I think maybe he wanted us to actually write a string and fill it with the actual characters on our keyboard but I have no idea how to get special characters out of my keyboard so I just left it to the for loop.

Wednesday, 25 October 2023

Chapter 27 // Exercise 6, 7 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022.

Chapter 27 // Exercise 6

Change the representation of Link and List from section 27.9 without changing the user interface provided by the functions. Allocate Links in an array of links and have the members first, last, pre, and suc be ints (indices into the array).

I honestly had no idea what he meant when I read this so I asked ChatGPT to explain it to me like I'm 5. It was very helpful and I realised that what he wanted us to do was kind of create a vector where you can traverse the list via an index and then access the data at that index.

I had a stab at it for a couple of hours but honestly, my head just wasn't in it. I cannot stand anything to do with linked lists, never mind linked lists in C. So I left it half finished. The code I linked above kind of works but it also kinda doesn't.

Chapter 27 // Exercise 7

What are the advantages and disadvantages of intrusive containers compared to C++ standard (non-intrusive) containers? Make a list of pros and cons.

Github: N/A

I won't write an entire essay but from what I've seen intrusive containers would've been very normal back when C ruled the land. They're extremely lightweight versions of C++ templated containers, only you don't get the flexibility/generic-ness of the templated container but you get better performance. 

I suppose they would be useful in embedded systems where you know exactly what you're working with and have specific memory constraints. 

Tuesday, 24 October 2023

Chapter 27 // Exercise 4, 5 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2022.

Chapter 27 // Exercise 4

If you didn't already, write a C++ version of the intrusive List example in section 27.9 and test it using every function.

This took me a good hour. I still hate linked lists. For this one I decided to make the list templated instead of Name storing a linked list. All the functions are now part of the list and I considered having it use a std::unique_ptr but in the end decided to just go with new and delete as the whole point of an intrusive list is to be as efficient as possible.

Chapter 27 // Exercise 5

Compare results of exercises 3 and 4.

Github: N/A

I think it's obvious that I much prefer exercise 4. I don't like having all the functions on their own, the classes aren't managing their own memory and the syntax to create new ones of different types is just hideous. 

Wednesday, 27 September 2023

Chapter 27 // Exercise 2, 3 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and ISO C11 Standard.

Chapter 27 // Exercise 2

Complete the intrusive List example in section 27.9 and test it using every function.

This was quite a painful exercise. Mainly because I hate linked lists and they confuse me with their mPrev->mNext->mPrev malarky but this time I had to do it in C! C also just has so many parenthesis.

Chapter 27 // Exercise 3

"Pretty up" the intrusive List example in section 27.9 as best you can to make it convenient to use. Do catch/handle as many errors as you can. it is fair game to change the details of the struct definitions, to use macros, whatever.

So I turned the code into my usual style and found a bug? I'm not sure but I couldn't get it to run using the code in the book. In the main() function it needs to be struct Link* curr. I think he did that on purpose though as the for loop doesn't work otherwise because a List doesn't have suc as a member variable.

Apart from that, I didn't really bother with the other things.

Chapter 27 // Exercise 2 - Principles & Practice Using C++


Monday, 28 August 2023

Chapter 27 // Exercise 1 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and ISO C11 Standard.

Chapter 27 // Exercise 1

Implement versions of strlen(), strcmp(), strcpy().

I feel like I'm being punished. Please let me go back to C++, I swear I'll be good. 




Thursday, 20 July 2023

Chapter 27 // Drill 1, 2, 3 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and ISO C11 Standard.

Chapter 27 // Drill 1

Write a "Hello, World!" program in C, compile it, and run it.

The last time I did this, I used Visual Studio Code and had to set up my own build script and call it on the command line. I'm much lazier now and I'm just using Visual Studio Community.


Chapter 27 // Drill 2

Define two variables holding "Hello" and "World!" respectively; concatenate them with a space in between; and output them as Hello World!

I was a bit embarrassed that it took me a good 25 minutes to do this exercise lol. I got to "define 2 variables" and was like "how do string again??" I also got caught out by the missing terminating nulls and using strcpy instead of strncpy.


Chapter 27 // Drill 3

Define a C function that takes a char* parameter p and an int parameter x and print out their values in this format: p is "foo" and x is 7. Call it with a few argument pairs.

A little bit more what I'm used to. I absolutely despise that Unreal Engine uses printf so prolifically. I love cout << and I'm not afraid to say it.





Wednesday, 19 July 2023

Chapter 26 // Exercise 12, 13, 14 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 12

Write a program that generates random floating-point numbers and sort them using std::sort. Measure the time used to sort 500,000 doubles and 5,000,000 doubles.

This was a fun little task and taught me how to create a function to create random doubles.


Chapter 26 // Exercise 13

Repeat the experiment in the previous exercise, but with random strings of lengths in the [0:100) range.

I'd already created a randString function many exercises earlier and added it to std_lib_facilities. So you can find that in the link at the top of the post. Creating the vectors took almost as much time as sorting them. Especially the second one. I watched the memory used jump to 1.1GB and it took a good 120 seconds to create the strings.


Chapter 26 // Exercise 14

Repeat the previous exercise, except using a map rather than a vector so that we don't need to sort.

I had to add an arbitrary value for each map entry. As its added sorted though, I timed creating the map. I found it weird though that sometimes, my strings would all start with a number; especially with 500000 strings created.

I also found it interesting that the map.size() was reporting 487,784 elements added when 500,000 were inserted. This meant that randString() created almost 13,000 duplicate strings; which is wild.

I could've used a container like Set, which only stores keys but the exercise said map. I also could've stored the strings as value and then sorted by value but then that defeats the purpose of a map.


This was much faster than the vector though, as it took 120 seconds to create the vector then the extra 80 to sort. However, I am missing a chunk of data due to keys being the same.

...

And with that, I'm done with Chapter 26! I honestly can't believe there's only 1 chapter left. It feels like a chapter of my life is coming to an end and I don't think I'm ready.

Tuesday, 18 July 2023

Chapter 26 // Exercise 11 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 11

Time the sum example from section 26.6 with m being square matrices with dimensions 100, 10,000, 1,000,000, and 10,000,000. Use random element values in the range [-10:10). Rewrite the calculation of v to use a more efficient (not O(N2)) algorithm and compare the timings.

I literally had no memory of how to use the matrix code from Chapter 24 at all. It was practically a year ago for me at this point. It took me a while to figure out how to use it again and what to do.

row_sum needs to be implemented. After that I had trouble creating the random matrices of certain values as I kept getting bad memory allocations and I was only at a matrix of size 10,000. I'm pretty sure Bjarne did this intentionally, or he was using a super computer, as mine kept running out of memory trying to allocate values into the matrices. I did some quick calculations and realised this is because a matrix of size 10,000 x 10,000 is trying to allocate memory for 100,000,000 doubles. A double is 8 bytes, so we're roughly trying to allocate memory for 762 MB of memory in one go. For the next size up of 1,000,000 that would equate to 7.45 terabytes!

Yeah, Bjarne definitely did this on purpose. So instead of creating these large matrices, I decided to "simulate" the large matrix by just computing the sum. However, this is still very slow and will take forever past 10,000. 

Ultimately I don't believe there is a way to use a more efficient algorithm because summing all the elements in a matrix is O(N^2). All we can do is try and make the code more efficient like passing by reference or using multithreading.


Monday, 17 July 2023

Chapter 26 // Exercise 9, 10 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 9

Add a text-based output format for the graphics interface library. For example, when a call Circle{Point{0,1},15} is executed, a string like Circle{Point{0,1},15} should be produced on an output stream.

For this, I didn't know if he meant output to the window (so a cout stream of sorts) or output it to a file (so ostream). So I decided on an output file based on the next exercise.

I used my "FLTK Engine" for this exercise again:

And added a simple call to output to a text file called DebugCommandsCalled. You can find this change in Window.cpp Blink::Window::onTextEnteredInCommandConsole().

Chapter 26 // Exercise 10

Use the text-based interface from exercise 9 to write a better test for the graphical interface library.

Github:  N/A

So we don't have any tests to start with for the FLTK code and I can't really imagine how using text files would be useful in the long run. I'd be much more inclined to add google tests to the project and start using that instead.

Sunday, 16 July 2023

Chapter 26 // Exercise 8 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 8

Add a text-based interface to the graphics interface library from Chapters 12-15. For example, the string Circle{Point{0,1},15} should generate a call Circle{Point{0,1},15}. Use this interface to make a "kid's drawing" of a two-dimensional house with a roof, two windows, and a door.

So I cheated on that first graphics exercise back in Chapter 12 Exercise 7 and drew it in paint. Then loaded it as an image.

I had recently though, re-written the helper code from Bjarne into my own library of sorts (Adventures With Tetris) so going back to that messy, all over the place code from 2020 was jarring. Graph is so big now, if you make a change to it, compilation time is actually noticeable as everything includes Graph.h.

The re-write was the start of a planned FLTK Game Engine I named "Blink". You can find it here:
Apparently Blink is already the name of a HTML engine, however it's developed by Google, so it'll probably be dead in a few years.
(EDIT 09/10/2023 - I've now renamed the engine BLNK)

For this exercise I thought it would be useful to open a "console command" box when you hit a button on the keyboard. Like in Unreal Engine when backtick is pressed you can enter console commands.

This also seemed like a useful thing to add to the engine, so using UE for inspiration, I created a BasicInputBox which is a widget that takes in text and you can grab the contents of the box in various formats (int, string, double etc). I then added an instance of a BasicInputBox to a Window calling it a "CommandConsole". 

I then overrode Fl_Window::handle() to listen for keyboard events. When backtick is pressed it simply shows the widget, making it look like the console window has been opened. You can type text into it. Then with the focus still in that widget, you press enter and the callback function associated with the widget is triggered. The callback grabs what's in the box and uses it in someway. So for example, if you pass "r.drawCircle 100 100 50" to the Command Console, it draws a Circle at x100, y100 with a radius of 50.

Chapter 26 // Exercise 8 - Principles & Practice Using C++

I then got bored of the task as I really want to get this book finished...someday. And so I added a quick method for Images. "r.drawImage 100 100 house.png" draws the image specified in the last parameter at x100, y100. Mirroring what I did in chapter 12 exercise 7 all those years ago.

Chapter 26 // Exercise 8 - Principles & Practice Using C++

It's not perfect. I'd prefer to not have the window control what the command console does. A future task would be to send the info to a Console Manager along with the window. Using callbacks it can then call the appropriate callback, send the window in and the type can draw to the window. Or you can do something else, like change the colour of the background.

Saturday, 1 July 2023

Chapter 26 // Exercise 7 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 7

Test the "simple text editor" from section 20.6.

I can't believe I did Chapter 20 almost 2 years ago now...I'm really slacking trying to get my way through this book.

Anyway, I went and got my code from exercise 6, chapter 20 and it no longer compiles for some reason. I had done nothing to it and I'm still using VS2019. It would appear that in the past 2 years, custom iterator classes need more definitions and I couldn't be bothered doing that. So I just re-wrote find_text to not use std::find.

I did a fair amount of testing. I didn't want to spend too much time on it though.

Friday, 30 June 2023

Chapter 26 // Exercise 6 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 6

Modify the calculator from Chapter 7 minimally to let it take input from a file and produce output to a file (or use your operating systems facilities for redirecting I/O). Then devise a reasonably comprehensive test for it.

Chapter 7 is back; it's the gift that keeps on giving.

I already kind of implemented this back in Chapter 16 when we had to give this code some graphics using FLTK in C16 Exercise 9. I basically took the calculator code; did nothing to it except "fake" a cin buffer by creating an istringstream and passing the binary data directly to the cin buffer:
 string calculate(string input) 
 { 
     //put the input "into" cin 
     istringstream oss(input); 
     cin.rdbuf(oss.rdbuf());
    
     //now do normal calculator stuff as we have "characters in the buffer" 
     string answer = to_string(statement());
     //clean up token stream for next statement 
     ts.ignore(); 
     return answer; 
 }

Using this code (Exercise 9) I made it so it reads from from a text file that takes input per line, then compares it to the expected output from the next line and compares the two. I decided to just read everything in from one file instead of outputting to another file.

Amusingly, I used ChatGPT-4 to generate the test list and as I ran the tests it gave an incorrect answer for sqrt(pow(5,3)). it said it was 8.66 instead of 11.18. I've decided to leave that in so you can see a failure.

Monday, 15 May 2023

Chapter 26 // Exercise 5 - Principles & Practice Using C++

In this exercise I'm using Visual Studio 2019 and a modified version of the std_lib_facilities header found here.

Chapter 26 // Exercise 5

Add a test to the set of binary_search tests to try and catch the (unlikely) error of a binary_search modifying the sequence.

I was a bit confused by this as std::binary_search does not modify anything passed to it. The sequence must already be sorted (or it won't work properly). I tried googling for this and couldn't find any information on it. I then asked ChatGPT-4 and it said std::binary_search will not modify but could provide undefined behaviour if the sequence wasn't sorted.

I then thought that maybe, the predicate function you can pass to binary_search would be able to modify the sequence in some way however the variables are passed to the predicate by const so you would have to const_cast to change them which is considered very bad.

I then told Chat this and gave it the name of the book, the author and the exercise and it pretty much told me "you're reading into this too much, you're supposed to just write a test that shows it doesn't modify the sequence". Should've added "duh" at the end as well.

Once again, taking things literally and overthinking was not the answer.

So instead in TestAll I made a local copy of the sequence then added an if that checks if the sequence has changed after the binary_search was done. No new test needed.