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

Monday, 8 March 2021

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

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

Chapter 19 // Exercise 16

Sometimes, it is desirable that an empty vector be as small as possible. For example, someone might use vector<vector<vector<int>>>  a lot but have most elements vectors empty. Define a vector so that sizeof(vector<int>) == sizeof(int*), that is, so that the vector itself consists only of a pointer to a representation consisting of the elements, the number of elements, and a space pointer.


I was a bit confused at first due to the wording as I thought he meant to have 2 pointers but it could only be the size of 1 pointer. But, after re-reading the question several times realised he meant for the vector representation to hold all three.

I basically reused the vector created in exercises 8 and 9 but took out the allocator and used new and delete instead. This created an entire evening of nightmares. If I didn't understand the difference between copy/move constructors and copy/move assignment before; I sure as hell do now.

I ended up using the crt debug library to track down the exact new that was causing me problems and I realised that my move assignment was not deleting properly. I've left the crt stuff in the code; it's an extremely useful tool.


I also became a pedantic about my vectors. I wanted the original vector to be a fully functional vector in it's own right and the SmallVector to just rely on all of OriginalVector's functions. I think that's what caused me the headache as I tried to separate in my head what was an allocation and what wasn't. 

Ultimately, a frustrating yet extremely rewarding exercise.

When I ran the code a vector<vector<vector<int>>> reported a size of 16 and my SmallVector<SmallVector<SmallVector<int>>> was 4.

And with that, Chapter 19 is finally over. I started it on the 22nd of December and it was beginning to feel like the chapter that would never end. That said I've been doing about 3000 other projects in-between. I really need to focus on this book more.

Sunday, 7 March 2021

Chapter 19 // Exercise 14, 15 - Principles & Practice Using C++

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

Chapter 19 // Exercise 14

Provide a GUI interface and a bit of graphical output to the "Hunt the Wumpus" game from the exercises in Chapter 18. Take the input in an input box and display a map of the part of the cave currently known to the player in a window.

Chapter 19 // Exercise 15

Modify the program from the previous exercise to allow the user to mark rooms based on knowledge and guesses, such as "maybe bats" and "bottomless pit."

Github: 

I was both happy and filled with dread at this exercise as my code had been hackily hard coded to suit a console window environment. I then almost gave up as I realised just how much code relied on _getch() pausing the window. After much drinking, I then remembered state machines are thing and hackily threw together a quick state system that forces the game to wait for presses. I then changed all the couts to a stringstream and pushed that to a new widget called Multiline_Outbox which was created from an Fl_Multiline_Outbox.

For the second part, I quickly added another input box where you can type "13p" or "13b" to mark it with a guess. There is no testing anywhere in this game for incorrect input so, put the right input in.

This is quite possibly some of the worst code I've ever written but I'm glad I didn't skip the exercises. I wanted to make it prettier but I also really want to move onto chapter 20.

Due to the amount of file changes I've uploaded the full project to github as well as the main files.

Chapter 19 // Exercise 14, 15  - Principles & Practice Using C++













Thursday, 4 March 2021

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

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

Chapter 19 // Exercise 13

Write a Tracer class where its constructor prints a string and its destructor prints a string. Give the strings as constructor arguments. Use it to see where RAII management objects will do their job (i.e., experiments with Tracers as local objects, member objects, global objects, objects allocated by new, etc.). Then add a copy constructor and a copy assignment so that you can use Tracer objects to see when copying is done.


You won't see the destructor calls for anything except the Tracer allocated by new because the rest are destroyed on return 0.

I did find out how to print line numbers in this exercise though. __LINE__ is a very handy macro. There are several others that you can use to print the file, time etc,.

Tuesday, 2 March 2021

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

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

Chapter 19 // Exercise 12

Define a File_handle class with a constructor that takes a string argument (the file name), opens the file in the constructor, and closes it in the destructor.


I'm not sure if he wanted us to use ofstream or ifstream or even the c-style FILE. Or if he wanted us to open a file that already exists or open a new file, or use FILE to guess? I decided to go with the simple option of a file to open that already exists seeing as how FILE has not been introduced.

Monday, 1 March 2021

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

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

Chapter 19 // Exercise 11

Design and implement a counted_ptr<T> that is a type that holds a pointer to an object of type T and a pointer to a "use count" (an int) shared by all counted pointers to the same object of type T. The use count should hold the number of counted pointers pointing to a given T

Let the counted_ptr's constructor allocate a T object and use a count on the free store. Let counted_ptr's constructor take an argument to be used as the initial value of the T elements. When the last counted_ptr for a T is destroyed, counted_ptr's destructor should delete the T.

Give the counted_ptr operations that allow us to use it as a pointer. This is an example of a "smart pointer" used to ensure that an object doesn't get destroyed until after its last user has stopped using it. Write a set of test cases for counted_ptr using it as an argument in calls, container elements, etc.


So I think this is basically a shared pointer and it took me a moment to wrap my head around what to do. The main key things are to ensure we're not allocating when copying (or increasing if the pointers are pointing at the same thing) as well as only delete the object and counter once all the other shared pointers have gone out of scope.

Thursday, 11 February 2021

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

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

Chapter 19 // Exercise 10

Implement a simple unique_ptr supporting only a constructor, destructor, ->, *, and release(). In particular, don't try to implement an assignment or copy constructor.


I left the crtDetectMemoryLeaks function in this one and it works for new and delete as well! Happy days. Glad I did as my first run leaked memory. I had forgotten which way round to delete in the destructor. I used to see a lot of code that would do:
delete object;
object = nullptr;

I messed it up and of course that memory was lost because I didn't stop to think. I did google to find out if it's standard practice to delete and null and the consensus is; not really as it may cover up double delete bugs. We've had problems with double deletes at work so I don't want to contribute to covering up those as they are nasty to solve.

Tuesday, 9 February 2021

Chapter 19 // Exercise 8, 9 - Principles & Practice Using C++

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

Chapter 19 // Exercise 8

Implement an allocator (section 19.3.7) using the basic allocation functions malloc() and free() (appendix B.11.4). Get vector as defined by the end of section 19.4 to work for a few simple test cases. Hint: Look up "placement new" and "explicit call of destructor" in a complete C++ reference.

Exercise 9

Re-implement vector::operator=() (section 19.2.5) using an allocator (section 19.3.7) for memory managment. (I ended up doing this one automatically as part of 8).

Github: 

I honestly had no clue on this one. It took me a couple of hours of just staring at chapter 19.

I looked up those terms in Bjarne's "The C++ Programming Language 4th ed." and he says that 'explicit calls of destructors should be avoided except in the implementation of resource management classes....However, it would be hard to implement an efficient general container along the lines of the standard-library vector without using explicit destructor calls.'

One very interesting thing I learnt during this exercise is how to detect memory leaks in visual studio when using malloc and free. After I had finished writing my class, I wanted to be absolutely positive that the allocated space was being freed properly so followed this guide to do so:

I then pushed back a few values and exited the program to be greeted with this in the output window:

Interestingly; the destructor for MyVector is not called on exiting main but it is called when MyVector is local to a function. I tested this with a std::vector and it to did not have it's destructor called at the end of main. Now I know for a fact that std::vector will clean up after itself properly so it must mean that this _CrtDumpMemoryLeaks() function dumps the output before MyVector has been destroyed. I'm happy that the class is properly allocating and freeing up memory though when it's local to other functions and I can see it being destroyed.

Then when testing I had a problem with values being destroyed when trying to print. I spent a few hours staring at my screen and eventually went to bed angry. The next day I posted my code and asked for some help in our chat channel at work and my colleagues came to the rescue. Turns out I wasn't actually destroying anything in destroy(). They taught me how to use placement new and call the destructor explicitly.

This exercises took me far longer than I expected but I learnt a ton.

Sunday, 31 January 2021

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

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

Chapter 19 // Exercise 7

Try your solution to exercise 2 with some Numbers


This one got me until I managed to find this post:

I then understood that I was trying to return a variable that was a common type between the two given template types. The compiler doesn't know that Number<int> and Number<double> are common types but Number does have ways to add themselves together and return a common type.

There is a way to define a common_type for user defined classes:

I used this to create common types for the combinations given in the exercise. It was working...in odd ways. I did some more testing and found my Number class broke spectacularly when doing Number<char> operations with any other type of number. 

It took me a while to realise it was because when assigning, it created a copy of the Number<type> with whatever type was the first operand. So for example:

When using ints and chars, it always produces 970 no matter the order. So I'm guessing that the expected behaviour of int * char is always to treat the char as an int. My Number type was not doing this due to default constructing and returning the wrong type.

I eventually found a fix for this by setting the return type of the operator overload to a common type between the two of them:

It looks hideous. I could get rid of some of the ugliness with a typedef but I left it this way for "clarity". The only problem with this, is that all combinations of common types must be declared for the class.

Thursday, 28 January 2021

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

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

Chapter 19 // Exercise 6

Repeat the previous exercise, but with a class Number<T> where T can be any numeric type. Try adding % to Number and see what happens when you try to use % for Number<double> and Number<int>.


During this exercise I realised that Concepts were supposed to have become a part of the standard for C++11 and C++14 but were dropped and only now are they being introduced in C++20 (they're still not here yet). Concepts would have been perfect for this exercise as it meant we could restrict Number class to only being numeric types.

I eventually ended up finding this post:

which led me to a function in <type_traits>

So you can stick a static_assert(is_arithmetic_v<T>, "that's not a numeric type") in your class. It will check at compile time if T is a numeric type and if it's not it will print out the error message in the output log instead of some horrible template error.

Very handy. Probably not as flexible as concepts however there are other useful checks you can do with functions in type_traits:

I then wanted to be able to add ints to doubles etc., like you can do with numeric types. You will get a compiler warning due to truncation but it works. As the template already restricts it to common numerical types I didn't have to bother using common_type and templated all the operator overloads to take in a different type. If it's not a numerical type it won't even compile. If you do operations on floats and ints though you get a horrible looking warning from the compiler but it runs and rounds down as expected.

As for modulo, I can't believe it's only now that I realised you can only use it for ints; it doesn't work for floating point values. You have to use fmod(). I tried to allow for it by checking if the type was floating point but the compiler doesn't really seem to care. I thought about it and even the standard double doesn't allow it so why should I?

Wednesday, 20 January 2021

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

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

Chapter 19 // Exercise 5

Define a class Int having a single member of class int. Define constructors, assignment, and operators +, -, *, / for it. Test it, and improve its design as needed (e.g., define operators << and >> for convenient I/O).


I think I went a bit overboard with the operator overloading on this one. I tried searching but I couldn't find if there is a way to reduce the amount of overloading as I only want two types allowed; int and Int. I thought about using type_traits and templating the class but then that would mean Int would need a type when declared which isn't in the brief.

Tuesday, 12 January 2021

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

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

Chapter 19 // Exercise 4

Modify class Link from section 19.9.3 to be a template with the type of value as the template argument. Then redo exercise 13 from Chapter 17 with Link<God>.


I considered removing things that didn't make sense; like addOrdered() as Link can now be any type, how do we determine what a logical order is for any given type? Especially custom ones but then I remembered operator overloading. If we had Link<int>, addOrdered() would add it in numerical order as the check is is link1 less than link2. A string would be added in alphabetical order etc, so I added a helper to God that allows for a less than check against two Gods, returning the one whose name comes before the other. I also added another one to check if Gods were equal. 

The find() function is a bit messy as you have to pass in a God but it keeps it nice and generic for lots of other types.

The main takeaway from this exercise though was learning that templated class member functions must be implemented in the header file; if you put them in the cpp it will fail when linking. (Or they must be implemented in the same file the class is declared).

Sunday, 10 January 2021

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

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

Chapter 19 // Exercise 3

Write a template class Pair that can hold a pair of values of any type. Use this to implement a simple symbol table like the one we used in the calculator (section 7.8).


.

Thursday, 7 January 2021

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

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

Chapter 19 // Exercise 2

Write a template function that takes a vector<T> vt  and a vector<U> vu as arguments and returns the sum of all vt[i]*vu[i]s.


This one confused me for a bit as I pondered the best way to compare types. I had previously found a predicate called is_same that checks if to templated types are the same. It's defined in the <type_traits> header. There is a plethora of good stuff in there, along with common_type. This predicate takes in two templated types and if they are similar enough (think ints and floats) then common_type will deal with operations for you.

This also works as a concept. In section 19.3.3 I got very confused as Concepts have only just been introduced in C++20 so the concepts he's referring to here must mean something else now. I think of them as template predicates. Useful but horrible to look at.

Wednesday, 6 January 2021

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

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

Chapter 19 // Exercise 1

Write a template function f() that adds the elements of one vector<T> to elements of another; for example, f(v1, v2) should do v1[i] += v2[i] for each element of v1.


My brain momentarily stopped working when I first read this exercise as I wondered how you check to see if a string is being added to a double. Or how you would define char + float? Or string + size_t? Then I remembered that a templated function with only one template argument can only be of 1 type...brains.