Sunday 24 June 2018

Blog Update// Current State of Affairs

Well, the second year of my degree is officially over. I can't believe it's been two years already. It feels like yesterday when I decided out of the blue to become a programmer and start learning C++. I now have a foundation degree in Software Engineering & Game Development, but I'm doing the one year top-up to achieve a Bachelors degree. Even though I have the entire summer ahead of me, I've already started work on my dissertation as I've chosen something rather ambitious.

The past two years have been something of an eye opener for me. I initially chose the course because on the second year it included a module on modding games engines and for me this sounded like the perfect job. It made me think of game mods like the Skyrim Thomas the Tank Engine or various GTA5 mods (like this hilarious one showcased by Funhaus, I've watched this a thousand times and it never gets old). However, when I eventually got to this module after a year and half, I absolutely detested it. We had to use Unreal Engine 4 and create a game within a team of 5. I can now say with absolute certainty; that I detest Unreal Engine with a burning passion. I also don't particularly like 3D modelling or creating game assets which is half the point of being a games developer. I find these aspects to be tedious and boring. 

My problem I suppose is that the first language I learnt was C++ but in a C-style. Principles & Practice by Bjarne Stroustrup is an excellent book for learning C++, however, by learning it in a C-Style, I have come to hate object orientated programming and much prefer the old procedural style that had to be used with older languages. My favourite module of the second year was 3D games programming in which I used DirectX9 and C++ to create a simple StarFox (1992) clone. My tutor was exasperated with me because I created my own procedural engine instead of utilising his fully object-orientated engine. He likes to remind me that having my engine code and game logic so firmly tied to each other is not a good thing. I tend to counter with the fact that no-one but me should be using my engine so it shouldn't really matter. However, if I plan to get a job with a games company, my style of programming will clearly become a problem. 

So, all of these experiences have forced me re-evaluate exactly what it is I want to achieve from programming. I love writing code, I honestly cannot see myself doing anything else now that I've started, however, I'm not sure if I can see myself becoming a games developer (apart from in my spare time). That is why I have decided to go on after my degree and complete a master's degree in computer science to broaden my programming field.

For my dissertation, I've chosen to create a games engine for the Super Nintendo utilising the SuperFX chip, as I find it extremely fascinating. I've already started learning Assembly x86 and have found it to be a more enjoyable language than C++ because it has to be done procedurally. I actually really love it. After I have grasped the basics, I'll be going on to learn 65c816 ASM, which is the microprocessor used in the Super Nintendo (or well, the Ricoh 5A22 is used in the SNES but it's based on this particular chip). I've been told this is a particularly difficult language and should not be used to learn Assembly off the bat, so I chose x86 instead. 

So far I've found this task to be challenging and rewarding, far more so than choosing a more game-related subject. Therefore, I will continue to post exercises from Principles & Practice as I complete them but it is not going to be my main focus (as I thought it would be). Learning Assembly and finishing a DirectX11 engine in preparation for my final year is going to be my main priority. Thank you to all those, who visit this blog though and who knows where I'll be a couple more years down the line.

Saturday 16 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 11



Design and implement a set of useful helper functions for the Date class with functions such as next_workday() (assume that any day that is not a Saturday or Sunday is a workday) and week_of_year() (assume that week 1 is the week with January 1 in it and that the first day of a week is Sunday).

(the above code on github is different to below as I re-did the book)

dateclass.h
//dateclass.h
#ifndef _DATECLASS_H_
#define _DATECLASS_H_

#include "std_lib_facilities.h"

enum class Month
{
 jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};

enum class Day
{
 monday = 1, tuesday, wednesday, thursday, friday, saturday, sunday
};

//forward declaration
Month returnMonth(int month);
Day returnDay(int day);

//9.8 The Date Class
class Date
{
public:
 Date();
 ~Date();
 Date(int y, Month m, int d);  //check for valid date & initialise

 //non-modifying operations:
 int day() const { return d; };
 Month month() const { return m; }
 int year() const { return y; }

 //modifying operations:
 void add_day(int n);
 void add_month(int n);
 void add_year(int n);

private:
 int y;
 Month m;
 int d;

 bool lastDay = false;
 bool endYear = false;
};

//helper functions
bool is_date(int y, Month m, int d);   //true for valid date
bool leapyear(int y);     //true if y is leap year

Day findDayOfWeek(const Date& d);   //returns the day for the given date
Day next_workday(const Date& d);   //retrieves the next work day
int week_of_the_year(const Date& d);   //returns the number of the week in the year
int howManyDaysSince(const Date& d);   //returns how many days since the start of the year

void printDay(const Day& day);    //prints out day in written form

ostream& operator<<(ostream& os, Date& d);
istream& operator>>(istream& is, Date& dd);

bool operator==(const Date& a, const Date& b);
bool operator!=(const Date& a, const Date& b);

//to help with finding day for any given date
struct stringInt
{
 int monthDay;
 int key;
};

#endif // !_DATECLASS_H_


dateclass.cpp
//dateclass.cpp

#include "dateclass.h"

vector<stringInt> monthKey
{
 { 3, 1 },{ 4, 2 },{ 5, 3 },{ 6, 4 },{ 7, 5 },{ 8, 6 },{ 9, 7 },{ 10, 8 },{ 11, 9 },{ 12, 10 },{ 1, 11 },{ 2, 12 }
};
vector<stringInt> dayKey
{
 { 7, 0 },{ 1, 1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 5, 5 },{ 6, 6 }
};

//default Date Constructor
Date::Date() 
{
 y = 2001;
 m = Month::jan;
 d = 1;
}

//deconstructor
Date::~Date() {}

//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
 //check that y m d is a valid date
 if (!is_date(y, m, d))
  cout << "Error, invalid date." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
   Date::m = returnMonth(mon);
   if (static_cast<int>(Date::m) == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }

 }
}

void Date::add_month(int n)
{
 for (int i = 0; i < n; ++i)
 {
  //if month is equal to 12, make it 1, otherwise ++
  int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1);
  Date::m = returnMonth(mon);
  if (static_cast<int>(Date::m) == 12)
   endYear = true;

  if (endYear)
  {
   //just increase year by one
   endYear = false;
   ++Date::y;
  }
 }
}

void Date::add_year(int n)
{
 for (int i = 0; i < n; ++i)
  ++Date::y;
}

//switch to return correct type of Month
Month returnMonth(int month)
{
 switch (month)
 {
 case 1:
  return Month::jan;
  break;
 case 2:
  return Month::feb;
  break;
 case 3:
  return Month::mar;
  break;
 case 4:
  return Month::apr;
  break;
 case 5:
  return Month::may;
  break;
 case 6:
  return Month::jun;
  break;
 case 7:
  return Month::jul;
  break;
 case 8:
  return Month::aug;
  break;
 case 9:
  return Month::sep;
  break;
 case 10:
  return Month::oct;
  break;
 case 11:
  return Month::nov;
  break;
 case 12:
  return Month::dec;
  break;
 default:
  cout << "Bad month" << endl;
 }
}

//switch to return correct day
Day returnDay(int day)
{
 switch (day)
 {
 case 7:
  return Day::sunday;
  break;
 case 1:
  return Day::monday;
  break;
 case 2:
  return Day::tuesday;
  break;
 case 3:
  return Day::wednesday;
  break;
 case 4:
  return Day::thursday;
  break;
 case 5:
  return Day::friday;
  break;
 case 6:
  return Day::saturday;
  break;
 default:
  cout << "Bad day" << endl;
 }
}

void printDay(const Day& day)
{
 switch (day)
 {
 case Day::sunday:
  cout << "Sunday\n";
  break;
 case Day::monday:
  cout << "Monday\n";
  break;
 case Day::tuesday:
  cout << "Tuesday\n";
  break;
 case Day::wednesday:
  cout << "Wednesday\n";
  break;
 case Day::thursday:
  cout << "Thursday\n";
  break;
 case Day::friday:
  cout << "Friday\n";
  break;
 case Day::saturday:
  cout << "Saturday\n";
  break;
 default:
  cout << "Bad day" << endl;
 }
}

//true for valid date
bool is_date(int y, Month m, int d)
{
 //check that y is valid
 if (y < 1900 || y > 2018)
 {
  cout << "Error, invalid year." << endl;
  return false;
 }

 //check that m is valid
 if (m < Month::jan || m > Month::dec)
  return false;

 //check that d is valid
 if (d <= 0)
  return false; //d must be positive

 int daysInMonth = 31;  //most months have 31 days

 switch (m)
 {
  case Month::feb:
   //if leapyear, make it 29, if not make it 28
   daysInMonth = (leapyear(y)) ? 29 : 28;
   break;
  case Month::apr: case Month::jun: case Month::sep: case Month::nov:
   daysInMonth = 30; //the rest have 30 days
   break;
 }

 if (daysInMonth < d)
  return false;

 return true;
}

//checks for leap year (gregorian calender)
bool leapyear(int y)
{
 //if divisible by 4 but not 100 - then leap year
 if (y % 4 == 0 && y % 100 != 0)
  return true;
 //if divisible by 4, 100 and 400 - then leap year
 if (y % 4 == 0 && y % 100 == 0 && y % 400 == 0)
  return true;

 else
  return false;
}

//find day of the week for any year using Zeller's Rule
Day findDayOfWeek(const Date& d)
{
 int day, month, year, century;
 
 //get day
 day = d.day();

 //get key for month
 for (int i = 0; i < monthKey.size(); ++i)
 {
  if (monthKey[i].monthDay == static_cast<int>(d.month()))
   month = monthKey[i].key;
 }

 //get last two digits of year
 year = d.year() % 100;
 if (month == 11 || month == 12)
  year -= 1;

 //get the century - first 2 digits of year
 string y = to_string(d.year());
 char y1 = y.at(0);
 char y2 = y.at(1);
 string y3;
 y3 += y1;
 y3 += y2;
 century = stoi(y3);

 int f = d.day() + ((13 * month - 1) / 5) + year + (year / 4) + (century / 4) - (2 * century);
 day = f % 7;

 //get which day it is
 for (int i = 0; i < dayKey.size(); ++i)
 {
  if (day == dayKey[i].key)
   day = dayKey[i].monthDay;
 }

 return returnDay(day);
}

//retrieves the next work day
Day next_workday(const Date& d)
{
 //find the current day
 Day current = findDayOfWeek(d);

 //find next day
 Date nextDay(d);
 nextDay.add_day(1);
 Day next = findDayOfWeek(nextDay);
 while (next == Day::saturday || next == Day::sunday)
 {
  nextDay.add_day(1);
  next = findDayOfWeek(nextDay);
 }

 return next;
}

//returns how many days since the start of the year
int howManyDaysSince(const Date& d)
{
 int day = d.day();
 int month = static_cast<int>(d.month());
 cout << "Month: " << month << endl;
 int year = d.year();
 vector<int> daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 vector<int> daysInMonthLeap = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

 int result = 0;
 if (leapyear(year))
 {
  for (int i = 0; i < month - 1; ++i)
   result += daysInMonthLeap[i];
  result += day;
 }
 else
 {
  for (int i = 0; i < month - 1; ++i)
   result += daysInMonth[i];
  result += day;
 }

 return result;
}

//finds out what number week of the year it is
int week_of_the_year(const Date& d)
{
 int daysSince1st = howManyDaysSince(d); //how many days have passed since jan 1st of given year?
 cout << "Days since 1st: " << daysSince1st << endl;
 int dow;     //day of week. sun = 0, mon = 1 etc..
 int dowJan1;     //day of week on jan 1st

 //find out day of week for current day
 Day day = findDayOfWeek(d);
 if (day == Day::sunday)
  dow = 0;
 else
  dow = static_cast<int>(day);

 //find out day of week for jan 1st of given year
 day = findDayOfWeek(Date(d.year(), Month::jan, 1));
 if (day == Day::sunday)
  dowJan1 = 0;
 else
  dowJan1 = static_cast<int>(day);

 //find week number
 int weekNum = ((daysSince1st + 6) / 7);
 if (dow < dowJan1)
  ++weekNum;
 return weekNum;
}

//compares dates
bool operator==(const Date& a, const Date& b)
{
 return a.year() == b.year()
  && a.month() == b.month()
  && a.day() == b.day();
}

bool operator!=(const Date& a, const Date& b)
{
 return !(a == b);
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}

//write into Date
istream& operator>>(istream& is, Date& dd)
{
 int y, m, d;
 char ch1, ch2, ch3, ch4;
 is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4;
 if (!is)
  return is;
 if (ch1 != '(' || ch2 != ',' || ch3 != ',' || ch4 != ')')
 {
  //format error
  is.clear(ios_base::failbit);  //set the failbit
  return is;
 }

 dd = Date(y, Month(m), d);  //update dd
 return is;
}

main.cpp
//main.cpp

#include "std_lib_facilities.h"
#include "dateclass.h"

int main()
{
 //intialise dates
 Date today(2018, Month::jun, 14);
 Date tomorrow(today);

 //increase day by one
 tomorrow.add_day(1);
 tomorrow.add_month(2);
 tomorrow.add_year(2);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 //prints todays day
 cout << "Today is a: ";
 printDay(findDayOfWeek(today));

 //print next working day
 cout << "The next working day is: ";
 printDay(next_workday(today));

 //print week number
 cout << "The week number is: " << week_of_the_year(today) << endl;

 keep_window_open();

 return 0;
}


This took much longer than I expected, mainly because I honestly (and naively) expected to be implementing just two functions. I ended up creating quite a few more than that. 

First up, in order to find the next work day, the day of the week had to be figured out. We enter a number as the day, how does the date class know what day it is? 

I went on a rabbit hole trying to figure this out. I eventually ended up using a formula called Zeller's Rule:
http://mathforum.org/dr.math/faq/faq.calendar.html
This had a few problems though as I needed to separate the year into the first and last two digits as well as assign keys to months and days. The keys were assigned by creating a new struct which allowed for two ints. Then the month-key pairs and day-key pairs were inputted manually. The function findDayOfWeek() returns an int, however I created another function which converts the day int into the Day enum value and returns that.

Just when I thought the hard part was over, he then wanted us to find what week number of the year it was. There are many different ways of doing this online however I eventually went with a function that figures out how many days have passed since the 1st of Jan of the given year and then a formula adapted from here:
https://stackoverflow.com/questions/274861/how-do-i-calculate-the-week-number-given-a-date

Getting the howManyDaysSince() to work properly was a bit of headache as there isn't really much out there from a C++ perspective. There was an excellent yet crazy answer from a commenter called Mmars from this site:
https://www.epochconverter.com/daynumbers
however I found this to be ridiculous to look at or even try and read. I prefer my brute force solution. I did go a bit further though than Bjarne's suggestion of assuming that the 1st of jan was a sunday. 

I've purposefully left that function to take any date in as I may change it in the future to calculate how many days since any given date. However, it will need some tweaking.

Friday 15 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 10



Implement leapyear() from section 9.8.

dateclass.h
//dateclass.h
#ifndef _DATECLASS_H_
#define _DATECLASS_H_

#include "std_lib_facilities.h"

enum class Month
{
 jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};

//forward declaration
Month returnMonth(int month);

//9.8 The Date Class
class Date
{
public:
 Date();
 ~Date();
 Date(int y, Month m, int d);  //check for valid date & initialise

          //non-modifying operations:
 int day() const { return d; };
 Month month() const { return m; }
 int year() const { return y; }

 //modifying operations:
 void add_day(int n);
 void add_month(int n);
 void add_year(int n);

private:
 int y;
 Month m;
 int d;

 bool lastDay = false;
 bool endYear = false;
};

//helper functions
bool is_date(int y, Month m, int d);  //true for valid date
bool leapyear(int y);    //true if y is leap year

ostream& operator<<(ostream& os, Date& d);
istream& operator>>(istream& is, Date& dd);

bool operator==(const Date& a, const Date& b);
bool operator!=(const Date& a, const Date& b);

#endif // !_DATECLASS_H_

dateclass.cpp
//dateclass.cpp

#include "dateclass.h"

//default Date Constructor
Date::Date() 
{
 y = 2001;
 m = Month::jan;
 d = 1;
}

//deconstructor
Date::~Date() {}

//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
 //check that y m d is a valid date
 if (!is_date(y, m, d))
  cout << "Error, invalid date." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
   Date::m = returnMonth(mon);
   if (static_cast<int>(Date::m) == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }

 }
}

void Date::add_month(int n)
{
 for (int i = 0; i < n; ++i)
 {
  //if month is equal to 12, make it 1, otherwise ++
  int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1);
  Date::m = returnMonth(mon);
  if (static_cast<int>(Date::m) == 12)
   endYear = true;

  if (endYear)
  {
   //just increase year by one
   endYear = false;
   ++Date::y;
  }
 }
}

void Date::add_year(int n)
{
 for (int i = 0; i < n; ++i)
  ++Date::y;
}

//switch to return correct type of Month
Month returnMonth(int month)
{
 switch (month)
 {
 case 1:
  return Month::jan;
  break;
 case 2:
  return Month::feb;
  break;
 case 3:
  return Month::mar;
  break;
 case 4:
  return Month::apr;
  break;
 case 5:
  return Month::may;
  break;
 case 6:
  return Month::jun;
  break;
 case 7:
  return Month::jul;
  break;
 case 8:
  return Month::aug;
  break;
 case 9:
  return Month::sep;
  break;
 case 10:
  return Month::oct;
  break;
 case 11:
  return Month::nov;
  break;
 case 12:
  return Month::dec;
  break;
 default:
  cout << "Bad month" << endl;
 }
}

//true for valid date
bool is_date(int y, Month m, int d)
{
 //check that y is valid
 if (y < 1900 || y > 2018)
 {
  cout << "Error, invalid year." << endl;
  return false;
 }

 //check that m is valid
 if (m < Month::jan || m > Month::dec)
  return false;

 //check that d is valid
 if (d <= 0)
  return false; //d must be positive

 int daysInMonth = 31;  //most months have 31 days

 switch (m)
 {
  case Month::feb:
   //if leapyear, make it 29, if not make it 28
   daysInMonth = (leapyear(y)) ? 29 : 28;
   break;
  case Month::apr: case Month::jun: case Month::sep: case Month::nov:
   daysInMonth = 30; //the rest have 30 days
   break;
 }

 if (daysInMonth < d)
  return false;

 return true;
}

//checks for leap year (gregorian calender)
bool leapyear(int y)
{
 //if divisible by 4 but not 100 - then leap year
 if (y % 4 == 0 && y % 100 != 0)
  return true;
 //if divisible by 4, 100 and 400 - then leap year
 if (y % 4 == 0 && y % 100 == 0 && y % 400 == 0)
  return true;

 else
  return false;
}

//compares dates
bool operator==(const Date& a, const Date& b)
{
 return a.year() == b.year()
  && a.month() == b.month()
  && a.day() == b.day();
}

bool operator!=(const Date& a, const Date& b)
{
 return !(a == b);
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}

//write into Date
istream& operator>>(istream& is, Date& dd)
{
 int y, m, d;
 char ch1, ch2, ch3, ch4;
 is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4;
 if (!is)
  return is;
 if (ch1 != '(' || ch2 != ',' || ch3 != ',' || ch4 != ')')
 {
  //format error
  is.clear(ios_base::failbit);  //set the failbit
  return is;
 }

 dd = Date(y, Month(m), d);  //update dd
 return is;
}

main.cpp
//main.cpp

#include "std_lib_facilities.h"
#include "dateclass.h"

int main()
{
 //intialise dates
 Date today(2016, Month::feb, 29);
 Date tomorrow(today);

 //increase day by one
 tomorrow.add_day(1);
 tomorrow.add_month(2);
 tomorrow.add_year(2);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 keep_window_open();

 return 0;
}


At first I wasn't sure how to go about this. However after googling "what determines a leap year" there is some very handy psuedocode on wikipedia to determine whether a year is a leap year or not.

https://en.wikipedia.org/wiki/Leap_year

Turns out a leap year can be determined if the year is divisible by 4 or 100. However centurial years must be divisible by 400 to be a leap year.

Please be aware in this exercise though that the add_day() function still only wraps at 31 days as a later exercise instructs us to create this function so I haven't fixed it yet. 

Thursday 14 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 9



Create a Library class. Include vectors of Books and Patrons. Include a struct called Transaction. Have it include a Book, a Patron, and a Date from the chapter. Make a vector of Transactions. Create functions to add books to the library, add patrons to the library, and check out books. Whenever a user checks out a book, have the library make sure that both the user and the book are in the library. if they aren't, report an error. Then check to make sure the the user owes no fees. if the user does, report and error. If not, create a Transaction, and place it in the vector of Transactions. Also write a function that will return a vector that contains the names of all the Patrons who owe fees.

Link to Project:
Exercise 9 Project


This exercise has been provided as the full project as there are several different files included. It has been separated into the following classes:

  • Book Class
  • Date Class (based off the last drill in exercise 9)
  • Patron Class
  • Library Class
  • Main
The Book, Date and Patron classes can only be accessed from within the Library class. The library class is created in main() in which books can be checked out by passing the ISBN number of the book and the card number of the patron. 

The error checking isn't great but it works for the scenario he has given us. I also refrained from adding extra features that he hadn't explicitly laid out in the exercise. 

You can create a library class in main() and from there 3 functions are available: checkOutBook(isbn, cardNum), printFeesOwed() and printLibrary().

checkOutBook(isbn, cardNum) takes in an isbn in the format n-n-n-x and a set of numbers and checks to see if these correspond with books and patrons added to the library. If they do and the patron has no fees it creates a transaction. If they don't match or the patron has a fee, no transactions are created and an error message is displayed.

New books and patrons can be added to the library, however currently they must be hard-coded in. Although it would be easy to create a new function in library which takes in the required fields and creates a new book. The same can be done with patrons.

I tried not to go to crazy on this, although I kind of wanted to.

Wednesday 13 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 8


Create a Patron class for the library. The class will have a user's name, library card number, and library fees (if owed). have functions that access this data, as well as a function to set the fee of the user. Have a helper function that returns a Boolean (bool) depending on whether or not the user owes a fee.

bookclass.h

//bookclass.h
#ifndef _BOOKCLASS_H_
#define _BOOKCLASS_H_

#include "std_lib_facilities.h"

//enum for genres
enum class Genre
{
 default = 1, fiction, nonfiction, periodical, biography, children
};

//class Book
class Book
{
public:
 Book();
 ~Book();

 Genre  getGenre() const { return m_genre; }
 string getISBN() const { return m_isbn; }
 string getTitle() const { return m_title; }
 string getAuthor() const { return m_author; }
 int    getDate() const { return m_date; }
 bool   getStatus() const { return m_checkedOut; }

 void setGenre(Genre genre) { m_genre = genre; }
 void setISBN(string num1, string num2, string num3, string char1);
 void setTitle(string title) { m_title = title; }
 void setAuthor(string author) { m_author = author; }
 void setDate(string year);

 void checkBookOut() { m_checkedOut = true; };
 void checkBookIn() { m_checkedOut = false; };

private:
 //m_ prefix to denote member variable
 Genre m_genre;
 string m_isbn, m_title, m_author;
 int m_date;
 bool m_checkedOut;
};

//overloads
bool operator==(const Book& a, const Book& b);
bool operator!=(const Book& a, const Book& b);
ostream& operator<<(ostream& os, const Book& book);


#endif // !_BOOKCLASS_H_

bookclass.cpp

//bookclass.cpp
#include "bookclass.h"

//--OVERLOAD OPERATORS--//

//compare ISBN numbers
bool operator==(const Book& a, const Book& b)
{
 return a.getISBN() == b.getISBN();
}

bool operator!=(const Book& a, const Book& b)
{
 return a.getISBN() != b.getISBN();
}

//print out book
ostream& operator<<(ostream& os, const Book& book)
{
 string genre;
 switch (book.getGenre())
 {
 case Genre::fiction:
  genre = "Fiction";
  break;
 case Genre::nonfiction:
  genre = "Non-Fiction";
  break;
 case Genre::periodical:
  genre = "Periodical";
  break;
 case Genre::biography:
  genre = "Biography";
  break;
 case Genre::children:
  genre = "Children";
  break;
 default:
  genre = "Default";
  break;
 }
 return os << "Title: "     << book.getTitle()  << endl
     << "Author: "  << book.getAuthor() << endl
     << "Genre: "     << genre            << endl
     << "Copyright: "  << book.getDate()   << endl
     << "ISBN: "  << book.getISBN()   << endl
     << "Checked out:" << book.getStatus() << endl
     << endl;
}

//constructor
Book::Book()
{
 m_genre = Genre::default;
 m_isbn = m_title = m_author = "";
 m_date = 0;
 m_checkedOut = false;
}

//deconstructor
Book::~Book() {}

//set the ISBN
void Book::setISBN(string num1, string num2, string num3, string char1)
{
 size_t check;
 //check first number
 check = num1.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num1;
  check = num1.find_first_not_of("0123456789");
 }

 //check second number
 check = num2.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num2;
  check = num2.find_first_not_of("0123456789");
 }

 //check third number
 check = num3.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num3;
  check = num3.find_first_not_of("0123456789");
 }

 //check last character
 check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN digit or letter, try again: ";
  cin >> char1;
  check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 }

 //set isbn
 m_isbn = "";
 m_isbn.append(num1);
 m_isbn.append("-");
 m_isbn.append(num2);
 m_isbn.append("-");
 m_isbn.append(num3);
 m_isbn.append("-");
 m_isbn.append(char1);
}

//set and check the year of copyright
void Book::setDate(string year)
{
 size_t check;
 check = year.find_first_not_of("0123456789");
 while (check != string::npos || year.size() != 4)
 {
  cout << "Not a valid year try again: ";
  cin >> year;
  check = year.find_first_not_of("0123456789");
 }

 m_date = stoi(year);
}


patronclass.h

//patronclass.h
#ifndef _PATRONCLASS_H_
#define _PATRONCLASS_H_

#include "std_lib_facilities.h"

//class patron
class Patron
{
public:
 Patron();
 ~Patron();

 string getName() const { return m_name; }
 int getCardNum() const { return m_cardNum; }
 float getLibFees() const { return m_libFees; }
 bool isFeeOwed() const { return m_feeOwed; }

 void setName(string name) { m_name = name; }
 void setCardNum(int num) { m_cardNum = num; }
 void setFees(float fee);
 
private:
 string m_name;
 int m_cardNum;
 float m_libFees;
 bool m_feeOwed;
};

//helper functions
ostream& operator<<(ostream& os, const Patron& patron);

#endif // !_PATRONCLASS_H_

patronclass.cpp

//patronclass.cpp

#include "patronclass.h"

//constructor
Patron::Patron()
{
 m_name = "";
 m_cardNum = 0;
 m_libFees = 0.00f;
 m_feeOwed = false;
}

//deconstructor
Patron::~Patron() {}

//set fee
void Patron::setFees(float fee)
{
 m_libFees = fee;
 if (m_libFees <= 0)
  m_feeOwed = false;
 else
  m_feeOwed = true;
}

//print out patron
ostream& operator<<(ostream& os, const Patron& patron)
{
 string owed;
 if (patron.isFeeOwed())
  owed = "Yes";
 else
  owed = "No";

 return os << "Name: "   << patron.getName() << endl
     << "Card Number: " << patron.getCardNum() << endl
     << "Fees Owed?: "  << owed << endl
     << "Fee Amount: "  << patron.getLibFees() << endl
     << endl;
}

main.cpp

//main

#include "bookclass.h"
#include "patronclass.h"

int main()
{
 Book lotr;
 Patron user1;

 lotr.setTitle("Lord Of The Rings");
 lotr.setAuthor("J.R.R Tolkien");
 lotr.setGenre(Genre::fiction);
 lotr.setDate("1954");
 lotr.setISBN("1111", "1111", "1111", "J");
 cout << lotr;

 lotr.setISBN("123", "0", "4444", "6");
 lotr.checkBookOut();
 cout << lotr;

 user1.setName("Gandalph The Grey");
 user1.setCardNum(123456789);
 user1.setFees(1.23);
 cout << user1;

 user1.setFees(0.00f);
 cout << user1;

 keep_window_open();

 return 0;
}

For this exercise I separated everything into their own files just to clean the code up a little bit. It also means I'll only be posting files that are updated in the following exercises. 

Tuesday 12 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 7


Create an enumerated type for the Book class called Genre. Have the types be fiction, nonfiction, periodical, biography, and children. Give each book a Genre and make appropriate changes to the Book constructor and member functions.

#include "stdafx.h"
#include "std_lib_facilities.h"

enum class Genre
{
 default = 1, fiction, nonfiction, periodical, biography, children
};

class Book
{
public:
 Book();
 ~Book();

 Genre  getGenre() const { return m_genre; }
 string getISBN() const { return m_isbn; }
 string getTitle() const { return m_title; }
 string getAuthor() const { return m_author; }
 int    getDate() const { return m_date; }
 bool   getStatus() const { return m_checkedOut; }

 void setGenre(Genre genre) { m_genre = genre; }
 void setISBN(string num1, string num2, string num3, string char1);
 void setTitle(string title) { m_title = title; }
 void setAuthor(string author) { m_author = author; }
 void setDate(string year);

 void checkBookOut() { m_checkedOut = true; };
 void checkBookIn() { m_checkedOut = false; };

private:
 //m_ prefix to denote member variable
 Genre m_genre;
 string m_isbn, m_title, m_author;
 int m_date;
 bool m_checkedOut;
};

//--OVERLOAD OPERATORS--//

//compare ISBN numbers
bool operator==(const Book& a, const Book& b)
{
 return a.getISBN() == b.getISBN();
}

bool operator!=(const Book& a, const Book& b)
{
 return a.getISBN() != b.getISBN();
}

//print out book
ostream& operator<<(ostream& os, const Book& book)
{
 string genre;
 switch (book.getGenre())
 {
 case Genre::fiction:
  genre = "Fiction";
  break;
 case Genre::nonfiction:
  genre = "Non-Fiction";
  break;
 case Genre::periodical:
  genre = "Periodical";
  break;
 case Genre::biography:
  genre = "Biography";
  break;
 case Genre::children:
  genre = "Children";
  break;
 default:
  genre = "Default";
  break;
 }
 return os << "Title: "      << book.getTitle()  << endl
    << "Author: "     << book.getAuthor() << endl
    << "Genre: "      << genre            << endl
           << "Copyright: "  << book.getDate()   << endl
           << "ISBN: "       << book.getISBN()   << endl
           << "Checked out:" << book.getStatus() << endl
           << endl;
}

//constructor
Book::Book()
{
 m_genre = Genre::default;
 m_isbn = m_title = m_author = "";
 m_date = 0;
 m_checkedOut = false;
}

//deconstructor
Book::~Book() {}

//set the ISBN
void Book::setISBN(string num1, string num2, string num3, string char1)
{
 size_t check;
 //check first number
 check = num1.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num1;
  check = num1.find_first_not_of("0123456789");
 }

 //check second number
 check = num2.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num2;
  check = num2.find_first_not_of("0123456789");
 }

 //check third number
 check = num3.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num3;
  check = num3.find_first_not_of("0123456789");
 }

 //check last character
 check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN digit or letter, try again: ";
  cin >> char1;
  check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 }

 //set isbn
 m_isbn = "";
 m_isbn.append(num1);
 m_isbn.append("-");
 m_isbn.append(num2);
 m_isbn.append("-");
 m_isbn.append(num3);
 m_isbn.append("-");
 m_isbn.append(char1);
}

//set and check the year of copyright
void Book::setDate(string year)
{
 size_t check;
 check = year.find_first_not_of("0123456789");
 while (check != string::npos || year.size() != 4)
 {
  cout << "Not a valid year try again: ";
  cin >> year;
  check = year.find_first_not_of("0123456789");
 }

 m_date = stoi(year);
}

int main()
{
 Book lotr;
 lotr.setTitle("Lord Of The Rings");
 lotr.setAuthor("J.R.R Tolkien");
 lotr.setGenre(Genre::fiction);
 lotr.setDate("1954");
 lotr.setISBN("1111", "1111", "1111", "J");
 cout << lotr;

 lotr.setISBN("123", "0", "4444", "6");
 lotr.checkBookOut();
 cout << lotr;

 keep_window_open();

 return 0;
}

I don't particularly like using enums, I find them tedious when you need to print out data with them. Pretty much the only way to print out the info as text is via a switch statement which takes up a lot of lines and can harbour bugs if not careful.

Sunday 10 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 6



Add operators for Book class. Have the == operator check whether the ISBN numbers are the same for two books. Have the != also compare the ISBN numbers. Have << print out the title, author, and ISBN on separate lines.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Book
{
public:
 Book();
 ~Book();

 string getISBN() const { return m_isbn; }
 string getTitle() const { return m_title; }
 string getAuthor() const { return m_author; }
 int    getDate() const { return m_date; }
 bool   getStatus() const { return m_checkedOut; }

 void setISBN(string num1, string num2, string num3, string char1);
 void setTitle(string title) { m_title = title; }
 void setAuthor(string author) { m_author = author; }
 void setDate(string year);

 void checkBookOut() { m_checkedOut = true; };
 void checkBookIn() { m_checkedOut = false; };

private:
 //m_ prefix to denote member variable
 string m_isbn, m_title, m_author;
 int m_date;
 bool m_checkedOut;
};

//--OVERLOAD OPERATORS--//

//compare ISBN numbers
bool operator==(const Book& a, const Book& b)
{
 return a.getISBN() == b.getISBN();
}

bool operator!=(const Book& a, const Book& b)
{
 return a.getISBN() != b.getISBN();
}

//print out book
ostream& operator<<(ostream& os, const Book& book)
{
 return os << "Title: "      << book.getTitle()  << endl
     << "Author: "     << book.getAuthor() << endl
           << "Copyright: "  << book.getDate()   << endl
           << "ISBN: "       << book.getISBN()   << endl
           << "Checked out:" << book.getStatus() << endl
           << endl;
}


//constructor
Book::Book()
{
 m_isbn = m_title = m_author = "";
 m_date = 0;
 m_checkedOut = false;
}

//deconstructor
Book::~Book() {}

//set the ISBN
void Book::setISBN(string num1, string num2, string num3, string char1)
{
 size_t check;
 //check first number
 check = num1.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num1;
  check = num1.find_first_not_of("0123456789");
 }

 //check second number
 check = num2.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num2;
  check = num2.find_first_not_of("0123456789");
 }

 //check third number
 check = num3.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num3;
  check = num3.find_first_not_of("0123456789");
 }

 //check last character
 check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN digit or letter, try again: ";
  cin >> char1;
  check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 }

 //set isbn
 m_isbn = "";
 m_isbn.append(num1);
 m_isbn.append("-");
 m_isbn.append(num2);
 m_isbn.append("-");
 m_isbn.append(num3);
 m_isbn.append("-");
 m_isbn.append(char1);
}

//set and check the year of copyright
void Book::setDate(string year)
{
 size_t check;
 check = year.find_first_not_of("0123456789");
 while (check != string::npos || year.size() != 4)
 {
  cout << "Not a valid year try again: ";
  cin >> year;
  check = year.find_first_not_of("0123456789");
 }

 m_date = stoi(year);
}

int main()
{
 Book lotr;
 lotr.setTitle("Lord Of The Rings");
 lotr.setAuthor("J.R.R Tolkien");
 lotr.setDate("1954");
 lotr.setISBN("1111", "1111", "1111", "J");
 cout << lotr;

 lotr.setISBN("123", "0", "4444", "6");
 lotr.checkBookOut();
 cout << lotr;

 keep_window_open();

 return 0;
}

I changed the get functions to const on this version to prevent errors when using const versions of Book in the operators. Also, instead of using an if statement to print out specific statements in relation to a book being printed out, it just prints out 0 or 1 now. Other than that, not much was changed in this version.

Friday 8 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 5



This exercise and the next few require you to design and implement a Book class, such as you can imagine as part of software for a library. Class Book should have members for the ISBN, title, author, and copyright date. Also store data on whether or not the book is checked out. Create functions for returning those data values. Create functions for checking a book in and out. Do simple validation of data entered into a Book; for example, accept ISBNs only if the form n-n-n-x where n is an integer and x is a digit or letter. Store an ISBN as a string.


#include "stdafx.h"
#include "std_lib_facilities.h"

class Book
{
public:
 Book();
 ~Book();

 string getISBN()   { return m_isbn; }
 string getTitle()  { return m_title; }
 string getAuthor() { return m_author; }
 int    getDate()   { return m_date; }
 bool   getStatus() { return m_checkedOut; }

 void setISBN(string num1, string num2, string num3, string char1);
 void setTitle(string title) { m_title = title; }
 void setAuthor(string author) { m_author = author; }
 void setDate(string year);
 
 void checkBookOut() { m_checkedOut = true; };
 void checkBookIn() { m_checkedOut = false; };

 void printStats();

private:
 //m_ prefix to denote member variable
 string m_isbn, m_title, m_author;
 int m_date;
 bool m_checkedOut;
};

//constructor
Book::Book()
{
 m_isbn = m_title = m_author = "";
 m_date = 0;
 m_checkedOut = false;
}

//deconstructor
Book::~Book() {}

//set the ISBN
void Book::setISBN(string num1, string num2, string num3, string char1)
{
 size_t check;
 //check first number
 check = num1.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num1;
  check = num1.find_first_not_of("0123456789");
 }

 //check second number
 check = num2.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num2;
  check = num2.find_first_not_of("0123456789");
 }

 //check third number
 check = num3.find_first_not_of("0123456789");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN number, try again: ";
  cin >> num3;
  check = num3.find_first_not_of("0123456789");
 }

 //check last character
 check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 while (check != string::npos)
 {
  cout << "Not a valid ISBN digit or letter, try again: ";
  cin >> char1;
  check = char1.find_first_not_of("0123456789ABCDEFGHIJKLMNOPQRSTUVWXY");
 }

 //set isbn
 m_isbn = "";
 m_isbn.append(num1);
 m_isbn.append("-");
 m_isbn.append(num2);
 m_isbn.append("-");
 m_isbn.append(num3);
 m_isbn.append("-");
 m_isbn.append(char1);
}

//set and check the year of copyright
void Book::setDate(string year)
{
 size_t check;
 check = year.find_first_not_of("0123456789");
 while (check != string::npos || year.size() != 4)
 {
  cout << "Not a valid year try again: ";
  cin >> year;
  check = year.find_first_not_of("0123456789");
 }

 m_date = stoi(year);
}

//print book details
void Book::printStats()
{
 cout << "Title: " << m_title << endl;
 cout << "Author: " << m_author << endl;
 cout << "Copyright: " << m_date << endl;
 cout << "ISBN: " << m_isbn << endl;
 if (m_checkedOut)
  cout << "Checked out: Yes" << endl;
 else
  cout << "Checked out: No" << endl;

 cout << endl;
}


int main()
{
 Book lotr;
 lotr.setTitle("Lord Of The Rings");
 lotr.setAuthor("J.R.R Tolkien");
 lotr.setDate("1954");
 lotr.setISBN("1111", "1111", "1111", "J");
 lotr.printStats();

 lotr.setISBN("123", "0", "4444", "6");
 lotr.checkBookOut();
 lotr.printStats();

 keep_window_open();

 return 0;
}


This was quite a confusing task as many assumptions had to be made. First, what kind of copyright date should be set? Generally the day and month are optional, so I decided to only accept the year of copyright. Second, how many integers could be allowed for the ISBN format? I'm not sure if he meant only 1 number or however many. For example 1-1-1-H, or 11111-45676543-4-7. I decided to go with the latter.

The hardest part was the check for the ISBN, however whilst searching how to solve turning a string into an int, I came across a method of std::string called 'find_first_not_of' which handily takes in a string of letters that you want included and then returns the position of the first letter not wanted. This made checking the ISBN parts easy, as the while is set to check if there is a position or not. If there is one, then an undesired character was entered, if there is no position then there were no unwanted characters. A very handy method.

Tuesday 5 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 4


Look at the headache-inducing last example of section 8.4. Indent it properly and explain the meaning of each construct. Note that the example does do anything meaningful; it is pure obfuscation.

#include "stdafx.h"
#include "std_lib_facilities.h"

//just ew
struct X
{
 //strangely returns 1 no matter what
 void f(int x)
 {
  struct Y
  {
   int f() { return 1; }
   int m;
  };

  int m;
  m = x; 
  Y m2;
  return f(m2.f());
 }

 int m;
 //if m, return 3 if not return whatever is in m + 2
 void g(int m)
 {
  if (m)
   f(m + 2);
  else
  {
   g(m + 2);
  }
 }

 X() {} //default constructor for X struct
 void m3() {} //define and declare m3, basically does nothing
 void main()
 {
  X a;
  a.f(2);  //return 1
 }

};

This code is so confusing that even though it doesn't do anything it still made me question myself. 

Sunday 3 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 3



Replace Name_pair::print() with a (global) operator <<  and define  ==  and !=  for Name_pairs.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Name_pairs
{
private:
 vector<string> name;
 vector<double> age;
public:
 void read_names(int iterator);  //read in a series of names
 void read_ages();     //read in age for name
 void sortNP();      //sort the vectors alphabetically by name
 vector<string> getNames() { return name; }
 vector<double> getAges() { return age; }
};

ostream& operator<<(ostream& os, Name_pairs& n) //print the vectors
{
 cout << endl;
 for (int i = 0; i < n.getNames().size(); ++i)
  os << "Name: " << n.getNames()[i] << "     Age: " << n.getAges()[i] << endl;
 return os;
}
//equals
bool operator==(Name_pairs& a, Name_pairs& b)
{
 return a.getNames() == b.getNames() &&
  a.getAges() == b.getAges();
}

//not equals
bool operator!=(Name_pairs& a, Name_pairs& b)
{
 return !(a == b);
}
void Name_pairs::read_names(int iterator)
{
 string names;
 for (int i = 0; i < iterator; ++i)
 {
  cout << "Name: ";
  cin >> names;
  name.push_back(names);
  cout << endl;
 }
}

void Name_pairs::read_ages()
{
 double ages;
 for (int i = 0; i < name.size(); ++i)
 {
  cout << "Age for " << name[i] << ": ";
  cin >> ages;
  age.push_back(ages);
  cout << endl;
 }
}

void Name_pairs::sortNP()
{
 vector<string> name_copy = name;
 vector<double> age_copy = age;

 sort(name.begin(), name.end());  //sort actual names

 //go through each member of sorted name
 for (int i = 0; i < name.size(); ++i)
 {
  //go through each member of original copy name
  for (int j = 0; j < name.size(); ++j)
  {
   //if sorted name matches original
   if (name[i] == name_copy[j])
   {
    //assign original age to new position to match sorted vector
    age[i] = age_copy[j];
   }
  }
 }
}

int main()
{
 cout << "How many names to read in? > ";
 int howMany;
 cin >> howMany;

 Name_pairs namePair;

 //read in names
 namePair.read_names(howMany);

 //read in ages
 namePair.read_ages();

 //print
 cout << namePair;

 //sort namePair alphabetically
 namePair.sortNP();

 //print
 cout << namePair;

 keep_window_open();

 return 0;
}


The most challenging part of this was getting the ostream operator to work properly. At the moment he's only shown examples for having it outside of the class but I'm pretty sure there is a way to have it inside the class as a member.

Also, he didn't particularly specify what type of == and != he wanted. Was to compare the name_pair in general or to compare specific days? I did it generally, however it could be improved to compare specific days and ages.

Friday 1 June 2018

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

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 1


Design and implement a Name_pairs class holding (name, age) pairs where name is a string and age is a double. Represent that as a vector<string>  (called name) and a vector<double> (called age) member. Provide an input operation read_names() that reads a series of names. Provide a read_ages() operation that prompts the user for an age for each name. Provide a print() operation that prints out the (name[i], age[i]) pairs (one per line) in the order determined by the name vector. Provide a sort() operation that sorts the name vector in alphabetical order and reorganises the age vector to match. Implement all "operations" as member functions.

#include "stdafx.h"
#include "std_lib_facilities.h"

class Name_pairs
{
private:
 vector<string> name;
 vector<double> age;
public:
 void read_names(int iterator); //read in a series of names
 void read_ages();  //read in age for name
 void print();   //print out vectors
 void sortNP();   //sort the vectors alphabetically by name
};

void Name_pairs::read_names(int iterator)
{
 string names;
 for (int i = 0; i < iterator; ++i)
 {
  cout << "Name: ";
  cin >> names;
  name.push_back(names);
  cout << endl;
 }
}

void Name_pairs::read_ages()
{
 double ages;
 for (int i = 0; i < name.size(); ++i)
 {
  cout << "Age for " << name[i] << ": ";
  cin >> ages;
  age.push_back(ages);
  cout << endl;
 }
}

void Name_pairs::print()
{
 cout << endl;
 for (int i = 0; i < name.size(); ++i)
  cout << "Name: " << name[i] << "     Age: " << age[i] << endl;
}

void Name_pairs::sortNP()
{
 vector<string> name_copy = name;
 vector<double> age_copy = age;

 sort(name.begin(), name.end());  //sort actual names

 //go through each member of sorted name
 for (int i = 0; i < name.size(); ++i)
 {
  //go through each member of original copy name
  for (int j = 0; j < name.size(); ++j)
  {
   //if sorted name matches original
   if (name[i] == name_copy[j])
   {
    //assign original age to new position to match sorted vector
    age[i] = age_copy[j];
   }
  }
 }
}

int main()
{
 cout << "How many names to read in? > ";
 int howMany;
 cin >> howMany;

 Name_pairs namePair;

 //read in names
 namePair.read_names(howMany);

 //read in ages
 namePair.read_ages();

 //print
 namePair.print();

 //sort namePair alphabetically
 namePair.sortNP();

 //print
 namePair.print();

 keep_window_open();

 return 0;
}

This was basically chapter 8 exercise 7 but in a class form, so I just reused some of the functions from that and stuck it into a class.