Showing posts with label chapter 25 exercises. Show all posts
Showing posts with label chapter 25 exercises. Show all posts

Monday, 5 December 2022

Chapter 25 // Exercise 17 - 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 25 // Exercise 17

In section 25.4.3-4 we provided a class Array_ref claimed to make access to elements of an array simpler and safer. In particular, we claimed to handle inheritance correctly. Try a variety of ways to get a Rectangle* into a vector<Circle*> using an Array_ref<Shape*> but no casts or other operations involving undefined behaviour. This ought to be impossible.

Github: N/A

Wow it's been a hot minute since I last looked at the FLTK code. I would avoid using arrays entirely but this little class is handy. There's no code for this one as none of it compiles anyway.

And with that I'm onto the second to last chapter of the book...it feels strange. What will I do without the exercises in this book hanging over my head?


Sunday, 4 December 2022

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

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

Chapter 25 // Exercise 16

Formulate 20 coding style rules (don't just copy those in section 25.6). Apply them to a program of more than 300 lines that you recently wrote. Write a short (a page or two) comment on the experience of applying those rules. Did you find errors in the code? Did the code get clearer? Did some code get less clear? Now modify the set of rules based on this experience.

This was a really interesting exercise for me as my coding style has changed so much over the past 7 years. I could probably make a video talking about this. It's even changed a lot over the past 3 years I've been putting my code on github.

Currently my 20 rules would be:
  1. Dashed lines between functions (I picked this up from work and now I will put bugs on code reviews if people have forgotten dashed lines)
  2. UpperCamelCase to be used for object creation, enums to use capital snake case and everything else should use lowerCamelCase (this is in direct violation of Bjarne's R302 but he can suck it, camelCaseForever. I also use Hungarian Notation for vectors sometimes because why be completely consistent?)
  3. All member variables should be prefixed with m
  4. All global variables should be prefixed with g
  5. Function argument parameters should be prefixed with p
  6. Enums should be prefixed with e and be all uppercase afterwards. Example; ePURPLE_CRAYON
  7. Where a function argument parameter is not modified in the function, it should always be passed by const reference (unless it's cheaper to just pass by const value)
  8. References should be used over pointers where possible
  9. Prefer std::vector over raw arrays
  10. const_cast should not be used
  11. Prefer ifndef/define over pragma once
  12. All local variables should be declared as const/constexpr unless they need to be modified
  13. Commonly used (non-changing) local variables should be declared in a private namespace in the cpp of the file they are used in
  14. Avoid mixing static member variables with non-static. Either create a namespace or singleton with all static functions/members
  15. Try to order member variables in descending size order
  16. Braces {} should always be on their own line. The only exception is 1 line functions in header files (or empty constructors/destructors)
  17. Avoid post-fix increment/decrement unless the functionality of post-fix is specifically needed. Always prefer pre-fix increment/decrement
  18. Includes should be in grouped order of local (""), then global (<>). Each group should be in alphabetical order
  19. Include what you use. Prefer including in the cpp with forward declares in the header. Only include in the header if you must
  20. Avoid lambda's. Only use them if you must
When I first started programming back in January 2016, I had no one to talk to about C++ and used this book or stackoverflow to try and complete exercises. When I started my degree that September, I started picking up habits from my tutors code and youtube videos. For those first 3 years my code style heavily resembled what you see in the book. Then I started working at Rare and at first I fought against the strict coding style/rules. I used them because I had to but over the past 3 and half years I've grown to love some of those rules, and the ones I don't like don't bother me anymore. I'd say my current style is the one I'll probably stick to now as I find it clear, concise and maintainable. 

I would say rules 1-6 are just me being picky and pedantic however the rest are extremely useful in production code and can even help you avoid bugs and provide a better debugging environment.

Saturday, 3 December 2022

Chapter 25 // Exercise 15 - 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 25 // Exercise 15

Measure the time (section 26.6.1) it takes to allocate 10,000 objects of random sizes in the [1000:0)-byte range using new; then measure the time it takes to deallocate them using delete.

Do this twice, once deallocating in the reverse order of allocation and once deallocating in random order. 

Then, do the equivalent  of allocating 10,000 objects of size 500 bytes from a pool and freeing them.

Then, do the equivalent of allocating 10,000 objects of random sizes in the [1000:0)-byte range on a stack and then free them (in reverse order). 

Compare the measurements . Do each measurement at least three times to make sure the results are consistent.

I split this up into several parts. For the object of random size I created a struct that holds a string, the string then reserves a random size between 0, 1000. A vector of that struct is created to hold the pointers to the object created.

---- Part 1 ----
(No pool or reserved allocation)
Allocate 10,000 objects: 1(19ms), 2(20ms), 3(21ms)
Deallocate in reverse order: 1(9ms), 2(10ms), 3(10ms)

---- Part 2 ----
(No pool or reserved allocation)
Allocate 10,000 objects: 1(20ms), 2(20ms), 3(21ms)
Deallocate in random order: 1(11ms), 2(11ms), 3(11ms)

For the ones with a pool I decided to not use new and delete and instead create a vector with a reserved size of 10,000 500-byte objects. That way when you pushback it won't need to allocate more space. Then "freeing" is as simple as just clearing the string.

---- Part 3 ----
(with reserved allocation of 500 bytes per object)
Allocate 10,000 objects: 1(14ms), 2(8ms), 3(8ms)
Deallocate in reverse order: 1(1ms), 2(1ms), 3(0ms)

---- Part 4 ----
(with reserved allocation of 500 bytes per object)
Allocate 10,000 objects: 1(14ms), 2(14ms), 3(14ms)
Deallocate in random order: 1(1ms), 2(1ms), 3(1ms)

---- Part 5 ----
(no pool, on std::stack)
Allocate 10,000 objects: 1(24ms), 2(24ms), 3(24ms)
Deallocate in random order: 1(15ms), 2(16ms), 3(16ms)

Allocating with a pool is always faster because the memory you need is right there. Memory pools are used a lot in games (you may hear them being called "banks" as well).

Friday, 2 December 2022

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

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

Chapter 25 // Exercise 14

Implement a simple vector that can hold at most N elements allocated from a pool. Test of for N === 1000 and integer elements.

I used my vector implementation from Chapter 19 - Exercise 8:

As this uses a custom allocator. I then modified the constructors to be able to take in 2 values; the number of elements required and the size of the allocator. If the size of the allocator given is more than 0 or more than the number of elements given, the constructor for MyVector will then reserve that space.

The allocator uses malloc, free and placement new. So on construction, it will allocate space based on the size of the allocator, then when we specify the number of elements, it will use placement new to "place" those elements into the already allocated memory.

Wednesday, 23 November 2022

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

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

Chapter 25 // Exercise 13

Use TEA (section 25.5.6) to communicate "securely" between two computers. Email is minimally acceptable.

Github: N/A

Using the program I created from here:

I created this "secure" message:

The key is "plumbus".

If you crack it, paste the message in the comments.

(If you're using my code, you'll have to edit the decipher function to read the correct text file).

Tuesday, 22 November 2022

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

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

Chapter 25 // Exercise 12

Write out the clear text of the example from section 25.5.6.

Github: N/A

Deciphering the text was part of Exercise 1. You can find the code here:

The deciphered text reads:
The Tiny Encryption Algorithm is one of the fastest and most efficient cryptographic algorithms in existence.
It was developed by David Wheeler and Roger Needham at the Computer Laboratory of Cambridge University.
It is a Feistel cipher which uses operations from mixed (orthogonal) algebraic groups - XOR, ADD and SHIFT in this case.
This is a very clever way of providing Shannon's twin properties of diffusion and confusion which are
necessary for a secure block cipher, without the explicit need for P-boxes and S-boxes respectively.
It encrypts 64 data bits at a time using a 128-bit key. It seems highly resistant to differential
cryptanalysis, and achieves complete diffusion (where a one bit difference in the plaintext will
cause approximately 32 bit differences in the ciphertext) after only six rounds. Performance on a
modern desktop computer or workstation is very impressive.

Monday, 21 November 2022

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

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

Chapter 25 // Exercise 11

Repeat the previous exercise, but keep the bits in a bitset<32>.
This was extremely easy and just involved converting the bitset to unsigned long longs in places. I like that you can subscript a bitset however, it's weird that you can't do other container-like things like set a range of the bitset.

Sunday, 20 November 2022

Chapter 25 // Exercise 10 - Principles & Practice Using C++

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

Chapter 25 // Exercise 10

Look at the bitfield example from section 25.5.5. Write an example that initialises a PPN, then reads and prints each field value, then changes each field value (by assigning to the field) and prints the result. Repeat this exercise, but store the PPN information in a 32-bit unsigned integer and use bit-manipulation operators (section 25.5.4) to access the bits in the word.
When I first read this, my immediate thought was "wtf?" Then I read it again, looked at the example and reminded myself it's just a simple bitfield exercise. Then I got to the second half where you have to read and write to an integer and that took me an hour to wrap my head around.

It's safe to say though, I now understand how shifting and masking works. It's actually much simpler that google makes it out to be. I hope my comments on the code show that. I found drawing it out on paper really helped visualise which bits to mask/shift.


Saturday, 19 November 2022

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

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

Chapter 25 // Exercise 9

Without using any standard headers (such as <limits>) or documentation, compute the number of bits in
an int and determine whether char is signed or unsigned on your implementation.


So to print the number of bits in an int we know that sizeof() will return us the number of bytes in a type so you can just multiply that by 8. I don't think this is cheating as sizeof is a keyword in C++ and doesn't require any standard headers to be used. 

For the second part, I used the example from page 964. It shows that char is default signed on my machine.

Friday, 18 November 2022

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

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

Chapter 25 // Exercise 8

Write out the numerical values of each character on your keyboard.


I cheated and just printed out all the ascii characters in a for loop as they're all on my keyboard anyway.

Thursday, 17 November 2022

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

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

Chapter 25 // Exercise 7

Write out the hexadecimal values from 0 to 400; write out the hexadecimal values from -200 to 200.


After the last exercise I honestly thought that this would trigger an infinite loop or something because Bjarne has that type of humour.

Wednesday, 16 November 2022

Chapter 25 // Exercise 5, 6 - Principles & Practice Using C++

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

Chapter 25 // Exercise 5

Write an infinite loop. Execute it.


Simple enough.

Chapter 25 // Exercise 6

Write an infinite loop that is hard to recognise as an infinite loop. A loop that isn't really infinite because
it terminates after completely consuming some resource is acceptable.


For this one I immediately though of a for loop going backwards but checking if the index was >= 0 (I'm not proud to say I've done this before...). However, Visual Studio is very smart these days and will give you an intellisense warning that the loop is infinite.

It also does the same for the old if(bool = false). I even googled for one but VS even recognised that as infinite. The best way to do it would be to just allocate memory in a recursive function.

Tuesday, 15 November 2022

Chapter 25 // Exercise 4 - Principles & Practice Using C++

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

Chapter 25 // Exercise 4

Add the bitwise logical operators &, |, ^, and ~ to the calculator from chapter 7.


Just when you thought it was safe, chapter 7 returns. I last touched that code 3 years ago now according to GitHub. I remember absolutely hating tokens and token streams because I thought they were some weird special type...it's just a class that gets characters from the cin buffer lol. I really love going back to old exercises as it makes me realise that I have learnt something after all.

I decided to go with the finished code from exercise 9:

And started by cleaning it up and putting it into seperate files. Then I added the bitwise operators  to my enum of symbols.

Now, as per the rules of the calculator, &, | and ^ are all expressions as they need primary's to operate on. However ~ is a prefix and can be used by itself so that makes it a primary. The main problem though is that you can't use (or shouldn't be using) bitwise operators on doubles as they're made up of 3 parts and our Tokens store all values as doubles.

To get round this I just cast everything to an int and then perform the operations but it prints a warning if the double wasn't safely converted to a float using std::modf();

I also made it so NOT requires parenthesis around the expression so you can do things like ~(5+4+sqrt(25)).

Chapter 25 // Exercise 4 - Principles & Practice Using C++


Monday, 25 July 2022

Chapter 25 // Exercise 3 - Principles & Practice Using C++

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

Chapter 25 // Exercise 3

Initialise a 32-bit signed integer with the bit patterns and print the result: 
- all zeros, 
- all ones,
- alternating ones and zeros (starting with a leftmost zero), 
- alternating zeros and ones (starting with a leftmost one),
- the 110011001100...pattern, 
- the 001100110011...pattern, 
- the pattern of all-one bytes and all-zero bytes starting with an all-one byte, 
- the pattern of all-one bytes and all-zero bytes starting with an all-zero byte. 
Repeat that exercise with a 32-bit unsigned integer.

.

Sunday, 24 July 2022

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

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

Chapter 25 // Exercise 2

Make a list of words that can be spelled with hexadecimal notation. Read 0 as o, read 1 as l, read 2 as to, etc.; for example Foo1 and Beef. Kindly eliminate vulgarities from the list before submitting it for grading.

Github: n/a

The first time I saw 0xdeadbeef at work whilst debugging, I thought I had done something terribly wrong. I was told it's often used to initialise unused memory and makes it "stand out" more when debugging.

I used an anagram solver for this one though:
And it gave me 1788 combinations...I won't list them all here but you can put: olizasgtbcdef into the textbox to see what it will generate.

Thursday, 21 July 2022

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

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

Chapter 25 // Exercise 1

If you haven't already, do the Try this exercises in this chapter.


Pg 939 Complete the program above and print out the addresses and sizes of the objects created to see if and how "holes" appear on your machine. If you have time, you might draw memory layouts likes the ones above to better visualise what's going on.

I couldn't get this one to work, or to visualise what he was showing in the book. When a message was deleted, a new node wouldn't take it's place in that freed up memory; it would go somewhere else. Sometimes it did though.

Pg 959 - Get the bits example to work and try out a few values to develop a feel for binary and hexadecimal representations. If you get confused about the representation of negative values, just try again after reading section 25.5.3.

The bits example wouldn't compile due to a narrowing conversion error. I had to add a static cast to the bitset constructor.

Pg 963 - The following example may look innocent, but it is an infinite loop:
void infinite()
{
unsigned char mx = 160; // very large
for(signed char i = 0; i < max; ++i) cout << int(i) << '\n';
}
Run it and explain why.

A signed char can be in the range of -127 to 127. An unsigned char can be 0 - 255. The signed char therefore will never reach 160 and it will wrap round causing the infinite loop.

Pg 965 - Draw out the bit patterns on a piece of paper. Using paper, then figure out what the answer would be for si=128. Then run the program to see if your machine agrees.

This one was interesting. I was expecting the signed chars to print -127 but they did -128 instead.

Pg 973 - The key was bs; what was the text?

This one took me a while as the code in the book enciphers and I kept wondering why my text file was filled with hex afterwards; turns out I should read the code more carefully.