Showing posts with label chapter 9. Show all posts
Showing posts with label chapter 9. Show all posts

Thursday, 22 August 2019

Chapter 9 // Exercise 16 - 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 16



Define an input operator (>>) that reads monetary amounts with currency denominations, such as USD1.23 and DKK5.00, into a Money variable. Also define a corresponding output operator.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

int main()
{
 Money amount1(c_GBP, 3.34);
 amount1.printMoney();

 Money amount2(c_USD, 786.789);
 amount1.printMoney();

 //read in new money
 cin >> amount1;
 cin >> amount2;

 //print new amounts
 cout << amount1 << endl;
 cout << amount2 << endl;

 cout << "\nPress any key to quit...";
 _getch();

 return 0;
}

moneyClass.h

//----------------------------------//
// moneyClass.h
//----------------------------------//
// for calculations involving money
//----------------------------------//
#ifndef _MONEYCLASS_H_
#define _MONEYCLASS_H_

//INCLUDES//
#include <string>
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

//----------------------------------//
// ENUM: Currencies
//----------------------------------//
enum Currencies
{
 c_GBP,
 c_USD
};

//----------------------------------//
// CLASS: Money
//----------------------------------//
class Money
{
public:
 Money();
 Money(Currencies currency, float amount);
 ~Money();

 void printMoney();

 Currencies getCurrency() { return m_currency; }
 void changeCurrency(Currencies currency) { m_currency = currency; }
 long int   getOutPutMoney() { return m_outputMoney; }
 double     getInputMoney()  { return m_inputMoney; }

private:
 //variables//
 Currencies m_currency;
 long int m_outputMoney; //cents
 double m_inputMoney; //dollars

};

//OPERATOR OVERLOADS//
void operator+(Money& money1, Money& money2);
ostream& operator<<(ostream& os, Money& money);
istream& operator>>(istream& is, Money& money);

#endif // !_MONEYCLASS_H_


moneyClass.cpp

//----------------------------------//
// moneyClass.cpp
//----------------------------------//
// for calculations involving money
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

//VARIABLES//
double gbp2usd = 1.26;
double usd2gbp = 0.79;


Money::Money() {}

Money::Money(Currencies currency, float amount) 
{
 m_currency = currency;
 m_inputMoney = amount;
}

Money::~Money() {}

//print output as dollars
void Money::printMoney()
{
 m_outputMoney = round(m_inputMoney * 100.0f);

 switch (m_currency)
 {
 case c_USD:
  cout << "-----USD----" << endl;
  cout << "Dollars: $" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Cents: " << fixed << setprecision(0) << m_outputMoney << endl;
  break;
 case c_GBP:
  cout << "-----GBP----" << endl;
  cout << "Pounds: £" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Pennies: " << fixed << setprecision(0) << m_outputMoney << "p" << endl;
  break;
 default:
  cout << "Bad output";
  break;
 }
}

//OPERATOR OVERLOADS//

//add currencies together - converts money if not the same
void operator+(Money& money1, Money& money2)
{
 cout << "\n";
 if (money1.getCurrency() == money2.getCurrency())
  //currencies are the same, no conversion needed, just add
  cout << fixed << setprecision(2) << money1.getInputMoney() + money2.getInputMoney();
 else if (money1.getCurrency() == c_GBP && money2.getCurrency() == c_USD)
 {
  //return pounds
  cout << fixed << setprecision(2) << "£" << (money2.getInputMoney() * usd2gbp) + money1.getInputMoney() << endl;
 }
 else if (money1.getCurrency() == c_USD && money2.getCurrency() == c_GBP)
 {
  //return dollars
  cout << fixed << setprecision(2) << "$" << (money2.getInputMoney() * gbp2usd) + money1.getInputMoney() << endl;
 }
}

//print out money
ostream& operator<<(ostream& os, Money& money)
{
 switch (money.getCurrency())
 {
 case c_GBP:
  return os << fixed << setprecision(2) << "\n£" << money.getInputMoney() << endl;
  break;
 case c_USD:
  return os << fixed << setprecision(2) << "\n$" << money.getInputMoney() << endl;
  break;
 default:
  return os << "Bad output";
  break;
 }
}

//read money into a Money variable
istream& operator>>(istream& is, Money& money)
{
 cout << "Please enter currency, followed by amount. For example GBP3.45" << endl;
 cout << "Currencies available: GBP || USD" << endl;

 string getMoney;
 cin >> getMoney;

 string denomination, amount;
 double mon;

 for (int i = 0; i < getMoney.size(); ++i)
 {
  if (i < 3)
   denomination += getMoney[i];
  else
   amount += getMoney[i];
 }

 //convert string to double
 mon = stod(amount);

 //update money variable
 if (denomination == "GBP")
 {
  money = Money(c_GBP, mon);
  money.changeCurrency(c_GBP);
 }
 else if (denomination == "USD")
 {
  money = Money(c_USD, mon);
  money.changeCurrency(c_USD);
 }
 else
 {
  cout << "bad input.";
 }

 return is;
}

My example is omitting some error checking due to time constraints so don't put in any illegal values otherwise it'll break however it does what he has asked for. When using cin >> with a Money variable it allows the user to input a new monetary value as well as change the currency. The << was already implemented in the last exercise. I also learnt in this exercise that there is a whole load of functions in the standard library for converting strings to almost any type of decimal. Bless the standard library.

Wednesday, 21 August 2019

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



Refine the Money class by adding a currency (given as a constructor argument). Accept a floating-point initializer as long as it can be exactly represented as a long int. Don't accept illegal operations. For example, Money*Money doesn't make sense, and USD1.23+DKK5.00 makes sense only if you provide a conversion table defining the conversion factor between U.S. dollars (USD) and Danish kroner (DKK).

...I literally have no idea what he wants me to do here. So we add a currency in the constructor, say GBP as a string along with a floating-point value. We've already been doing that but now we have to to do a check to ensure that it will fit in a long int. A long int is 32 bits in size and a double is 64 bits. (If anyone is interested, in a programming interview I was asked about the size of a struct and it contained all the fundamental types, so it's worth learning the sizes of each type). A float is 32 bits so perhaps we should change m_dollars to a float instead. The worst that would happen if we didn't is that we would lose some bits as they would get truncated to fit. I don't understand what he means by illegal operations though. Does he mean, don't allow Money*Money in the initialiser? It wouldn't compile anyway in the code as there is no definition for it. And for USD1.23+DKK5.00 where is this called? By the programmer writing the code or is it an argument passed to the program via input from the user? If it was the latter, I suppose you would take it in as a string and parse each value, then pass the currency and value to the money class to be constructed with that value. You can construct new classes at run time but it's far beyond what's been taught in the book so far. This is not well constructed exercise.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

int main()
{
 Money gbp(c_GBP, 3.34);
 gbp.printMoney();

 Money usd(c_USD, 786.789);
 usd.printMoney();

 gbp + usd;
 usd + gbp;

 cout << "\nPress any key to quit...";
 _getch();

 return 0;
}

moneyClass.h

//----------------------------------//
// moneyClass.h
//----------------------------------//
// for calculations involving money
//----------------------------------//
#ifndef _MONEYCLASS_H_
#define _MONEYCLASS_H_

//INCLUDES//
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

//----------------------------------//
// ENUM: Currencies
//----------------------------------//
enum Currencies
{
 c_GBP,
 c_USD
};

//----------------------------------//
// CLASS: Money
//----------------------------------//
class Money
{
public:
 Money(Currencies currency, float amount);
 ~Money();

 void printMoney();

 Currencies getCurrency()    { return m_currency; }
 long int   getOutPutMoney() { return m_outputMoney; }
 double     getInputMoney()  { return m_inputMoney; }

private:
 //variables//
 Currencies m_currency;
 long int m_outputMoney; //cents
 double m_inputMoney; //dollars

};

//OPERATOR OVERLOADS//
void operator+(Money& money1, Money& money2);
ostream& operator<<(ostream& os, Money& money);

#endif // !_MONEYCLASS_H_


moneyClass.cpp

//----------------------------------//
// moneyClass.cpp
//----------------------------------//
// for calculations involving money
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

//VARIABLES//
double gbp2usd = 1.26;
double usd2gbp = 0.79;

Money::Money(Currencies currency, float amount) 
{
 m_currency = currency;
 m_inputMoney = amount;
}

Money::~Money() {}

//print output as dollars
void Money::printMoney()
{
 m_outputMoney = round(m_inputMoney * 100.0f);

 switch (m_currency)
 {
 case c_USD:
  cout << "-----USD----" << endl;
  cout << "Dollars: $" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Cents: " << fixed << setprecision(0) << m_outputMoney << endl;
  break;
 case c_GBP:
  cout << "-----GBP----" << endl;
  cout << "Pounds: £" << fixed << setprecision(2) << m_inputMoney << endl;
  cout << "Pennies: " << fixed << setprecision(0) << m_outputMoney << "p" << endl;
  break;
 default:
  cout << "Bad output";
  break;
 }
}

//OPERATOR OVERLOADS//

//add currencies together - converts money if not the same
void operator+(Money& money1, Money& money2)
{
 cout << "\n";
 if (money1.getCurrency() == money2.getCurrency())
  //currencies are the same, no conversion needed, just add
  cout << fixed << setprecision(2) << money1.getInputMoney() + money2.getInputMoney();
 else if (money1.getCurrency() == c_GBP && money2.getCurrency() == c_USD)
 {
  //return pounds
  cout << fixed << setprecision(2) << "£" << (money2.getInputMoney() * usd2gbp) + money1.getInputMoney() << endl;
 }
 else if (money1.getCurrency() == c_USD && money2.getCurrency() == c_GBP)
 {
  //return dollars
  cout << fixed << setprecision(2) << "$" << (money2.getInputMoney() * gbp2usd) + money1.getInputMoney() << endl;
 }
}

//print out money
ostream& operator<<(ostream& os, Money& money)
{
 switch (money.getCurrency())
 {
 case c_GBP:
  return os << "\n£" << money.getInputMoney() << endl;
  break;
 case c_USD:
  return os << "\n$" << money.getInputMoney() << endl;
  break;
 default:
  return os << "Bad output";
  break;
 }
}

I'm still not entirely sure if this is what he wanted us to do but it takes in a currency (and you can add more to the enum, just provide converters in moneyClass.cpp). I even added some operator overloads to print out the money and add different currencies together.

Tuesday, 20 August 2019

Chapter 9 // Exercise 14 - 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 14



Design and implement a Money class for calculations involving dollars and cents where arithmetic has to be accurate to the last cent using the 4/5 rounding rule (.5 of a cent rounds up; anything less than .5 rounds down). Represent a monetary amount as a number of cents in a long int, but input and output as dollars and cents, e.g., $123.45. Do not worry about amounts that don't fit into a long int.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

int main()
{
 double cents, dollars;
 cout << "$";
 cin >> dollars;

 cents = dollars * 100.0f;
 cents = round(cents);

 cout << "Dollars: $" << fixed << setprecision(2) << dollars << endl;
 cout << "Cents: " << fixed << setprecision(0) << cents << endl;

 system("pause");
 return 0;
}

My first attempt I felt I had not done it correctly as it seemed a little too simple. It was the rounding rule that got me. Where did we need the code to round? When converting dollars to cents you just multiply it by 100. Then divide it by 100 to convert it back to dollars. I'm guessing he means that if someone inputs $2.298 he wants that figure rounded to $2.30 then stored as cents in a long int. I created a simple rounding program as seen above. This does exactly what he specifies. I then re-jigged it into the class format below as we expand on it in the next few exercises.

main.cpp

//----------------------------------//
// main.cpp
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

int main()
{
 Money money;
 money.menu();

 return 0;
}

moneyClass.h

//----------------------------------//
// moneyClass.h
//----------------------------------//
// for calculations involving dollars
// and cents
//----------------------------------//
#ifndef _MONEYCLASS_H_
#define _MONEYCLASS_H_

//INCLUDES//
#include <iostream>
#include <iomanip>
#include <conio.h>

using namespace std;

//----------------------------------//
// CLASS: Money
//----------------------------------//
class Money
{
public:
 Money();
 ~Money();

 void menu();

private:
 void getInput();
 void printDollars();

 void convertToCents();

 //variables//
 long int m_cents;
 double m_dollars;

};

#endif // !_MONEYCLASS_H_


moneyClass.cpp

//----------------------------------//
// moneyClass.cpp
//----------------------------------//
// for calculations involving dollars
// and cents
//----------------------------------//

//INCLUDES//
#include "moneyClass.h"

Money::Money() {}

Money::~Money() {}

//main menu
void Money::menu()
{
 cout << "-----Money Class----" << endl;

 getInput();
 convertToCents();
 printDollars();
}

//get input as $0.00
void Money::getInput()
{
 double input;
 cout << "Please enter dollars: \n$";
 cin >> input;

 m_dollars = input;
}

//convert dollars to cents
void Money::convertToCents()
{
 //100 cents == $1
 m_cents = round(m_dollars * 100.0f);
}

//print output as dollars
void Money::printDollars()
{
 cout << "Dollars: $" << fixed << setprecision(2) << m_dollars << endl;
 cout << "Cents: " << fixed << setprecision(0) << m_cents << endl;

 //return to main menu
 system("pause");
 system("CLS");
 m_cents = m_dollars = 0;
 menu();
}

A lot of people seem to go a bit crazy when it comes to rounding. The standard library provides a function called round() which generally adheres to the 4/5 rounding rule so there's no need to implement it yourself. Also, I used a float at first to store the currency however it came with some issues to I switched to a double to store the initial dollars in. I then just manipulated the output using cout functions to make them specific to certain decimal points.

Monday, 19 August 2019

Chapter 9 // Exercise 13 - 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 13



Design and implement a rational number class, Rational. A rational number has two parts: a numerator and a denominator, for example, 5/6 (five-sixths, also known as approximately 0.83333). Look up the definition if you need to. Provide assignment, addition, subtraction, multiplication, division and equality operators. Also, provide a conversion to double. Why would people want to use a Rational class?

This one took me a while as I planned out exactly how to go around it. Bjarne made the exercise vague enough for it to be interpreted a number of ways (pun intended). This was not a hard exercise, however it did make me think about class structure and the time was spent rearranging functions. This isn't perfect as I've omitted quite a few error checks, which you obviously wouldn't do if the public were to use this. 

I probably spent more time than is necessary on formatting but I just wanted it to look pretty. We could have gone the whole calculator route as well and done a new version which took in a string of say "4:5 * 7:5" with ':' denoting the separator as we'd need that sign for division however I instead left it for the user to just implement what they'd like in code.

This kind of class would be very useful to people like me who get flustered when they see things like "5/16ths of inch". I've never really been that great at working with fractions so something that works it out for you is quite useful. However, you could just use Google...like I do.

As I've used quite a few files, here is the full code drop:
https://github.com/l-paz91/principles-practice/tree/master/Chapter%209/Exercise%2013

EDIT 03/12/2019 - The link above is a link to a new version that I completed today. To be honest I can't even remember what I did now for the original but it sounds stupidly complicated.

I found the following sites useful:
Rational Numbers
Finding Greatest Common Divisor

Monday, 24 September 2018

Chapter 9 // Exercise 12 - 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 12



Change the representation of a Date to be the number of days since January 1, 1970 (known as day 0), represented as a long int, and re-implement the functions from section 9.8. Be sure to reject dates outside the range we can represent that was (feel free to reject days before day 0, i.e no negative days).

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
long int daysSince(const Date& date1, const Date& date2); //returns how many days between two given dates
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;
}

//this was wrong in previous chapters, I've realised this now and changed it
//new version of leapyear
bool leapyear(int y)
{
 //if century year, must be divisible by 400
 if (y % 400 == 0)
  return true;
 
 //if normal year and divisible by 4
 if (y % 4 == 0)
  return true;

 //else not leap year
 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;
}

//finds how many days have passed between two given dates
long int daysSince(const Date& date1, const Date& date2)
{
 long int daysSince = 0;
 Date tempDate = date1;

 //get how many years have passed between two dates
 int years = date2.year() - date1.year();

 //add number of days to daysSince for every year
 for (int i = years; i >= 1; --i)
 {
  //if leap year, add 366 days
  if (leapyear(tempDate.year()))
  {
   daysSince += 366;
  }
  //if not leap year only add 365
  else
   daysSince += 365;

  //increase tempDate so correct days are added
  tempDate.add_year(1);
 }

 //when we get to current year, calculate remaining days & add to total
 daysSince += howManyDaysSince(date2);

 return daysSince;
}

main.cpp
//main.cpp

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

int main()
{
 //intialise dates
 Date today(2018, Month::sep, 24);
 Date zero(1970, Month::jan, 1);

 //print how many days between 2 given dates, earliest date first
 cout << "Days since Jan 1st 1970: " << daysSince(zero, today) << endl;

 keep_window_open();

 return 0;
}


Ohhh kay. It's been a while. I didn't realise I had completely forgotten about this book over summer...whoops. Anyway, I'm going to try and do an exercise a night from now on. 

First things first, I realised that my implementation of leapyear() was completely wrong. I've now changed it and it's working properly now.

Now for the exercise, I didn't want to change the implementation of Date so instead I created a new function which takes two dates and works out how many days have passed since then. This new function is called daysSince(Date, Date). It takes two dates (no error checking just to keep code size down, so please put the earliest date first), then works out how many years are between the two. It then has a variable called daysSince which starts at 0. For every year either 365 or 366 (for leap years, checked using leapyear()) is added to it until we get to the current year. It then works out how many days since the start of the year using howManyDaysSince(Date) and adds that to the running total to get how many days between the two dates.

There are many sites on the internet which can work this out for you, however, they all seem to be out by a couple of days or even a month. I strongly believe my version is correct, if not, it is only out by one day. That I'm sure of. 

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.