Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

We would like to set aside just the right amount of storage, but the problem is,

ID: 3867608 • Letter: W

Question

We would like to set aside just the right amount of storage, but the problem is, we don’t know how long each c-string is until we load it from the file or ask the user for the information. So the simplest way to solve this problem is to create local temporary storage: char temp[strSize]; Now you are free to use istream.getline(temp, strSize); to load data, just like with assignment 3. Then you can find out how long the c-string is by calling strlen(): int len; len = strlen(temp); Now you can pass len along to the ‘new’ operator: exercises[i].name = new char[len + 1]; Notice that I used len + 1 inside of the square brackets. This is so there will be room for the null terminator. The last step is to copy the string from temp over to the brand new dynamically allocated c-string: strcpy(exercises[i].name, temp);

Continue to place your implementation and main function code into separate source code files, and include a header file in both source code files. You must have at least 2 source code files, but you may have more if you wish.

There is one other place, in the class, where we will be saving memory for assignment 4. Notice that we have updated the exercises array in the class (called exerciseJournal, above) from a struct array to a struct pointer array. That is why the asterisk (*) is between exerciseData and exercises for this version. Suppose we set arraySize to 100, but we only have 5 exercises loaded in the array. That would mean that we would be wasting space to store 95 exercises that we haven’t done. So instead of wasting a few hundred bytes for a struct, we will only be wasting 8 bytes for each pointer in the pointers array. Just as with the dynamic c-strings that you will be creating, the main difference with dynamic structs will be in the loadData() and add() methods.

Don’t use the string library or the vector library. Use the <cstring> library and dynamic c-strings. You will still need your global constants for string size (filename and temp string), and for array size (for the pointer array): const int strSize = 128; const int arraySize = 100;

Now that we will be using pointers for our structs, you will need to call ‘new’ before adding data to the array: exercises[countAndIndex] = new exerciseData; Also remember that you need to dereference the pointer to access the members of a dynamically allocated struct. So, one way to do that is: (*exercises[countAndIndex]).name; The parenthesis are there because the dot has higher precedence than the dereference asterisk. This code is fairly cumbersome, so C++ allows you to combine the asterisk and the dot into the dereference operator, which is ->. So, the other version is: exercises[countAndIndex]->name;

There is one other detail that you will need to take care of with this assignment, and that is the destructor. Notice that the destructor is listed as a prototype in the class public area. We need the destructor for this assignment, because dynamically allocated memory must be deallocated, or else there could be a memory leak. So inside of the destructor, for each struct that has been allocated, you will need to delete the name and other c-strings (delete[] name;), and then you will need to delete the struct (delete exercises[i];). There are no changes to time, calories and maxHeartRate, so you will not be calling delete with those member variables. I think that I have described the easiest way to create the destructor here, but the alternative is to create a destructor inside of the struct, as well as inside of the class. You may do so if you wish.

Dynamic memory can leak if you don’t deallocate memory correctly in your destructor. You can also have memory leaks if you allocate temporary storage dynamically anywhere else in your code, and then forget to call delete or delete[]. To check for memory leaks, use the utility called valgrind on the Linux system. Suppose your program is call exerciseApp. Then to check your program for leaks type:

valgrind ./exerciseApp on the command line. Valgrind gives you some information when it starts up, and then you can run your program normally. Test your program like normal, and then valgrind will give you a summary of leaks that it may have found. Here is an example where it found a 12-byte leak:

==17313== LEAK SUMMARY:

==17313== definitely lost: 12 bytes in 3 blocks

Explanation / Answer

main.cpp
---------------------------------------------------
/ qFH("What is the name of the exercise data text file to load?", fh);

#include <cstring>
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;

// Reusable stuff -------------------------------

const int STRING_SIZE = 64;
const int LIST_SIZE = 4096;

// Return true if any char in cs satisfies condition.
// condition has type 'int (*condition)(int)' to match <cstring> convention.
bool any(const char cs[], int (*condition)(int)) {
for (int i = 0; cs[i]; i++)
    if (condition(cs[i]))
      return true;
return false;
}

// The q prefix indicates a function that queries the user.

// Ask the user a question. Dump the response into answer.
void qCString(const char question[], char answer[],
              const int ss = STRING_SIZE) {
cout << question << ' ';
cin.getline(answer, ss);
}

// Bother the user until they enter a string containing graphical characters.
void qGCString(const char question[], char answer[],
               const int ss = STRING_SIZE) {
qCString(question, answer, ss);
while (!any(answer, isgraph))
    qCString("Try again:", answer, ss);
}

// Bother the user until they enter a valid integer. Return the integer.
int qInt(const char question[]) {
int resp;
bool fail;
cout << question << ' ';
while (true) {
    cin >> resp;
    fail = cin.fail();
    cin.clear();
    cin.ignore(STRING_SIZE, ' ');
    if (!fail)
      break;
    cout << "Try again: ";
}
return resp;
}

// Bother the user until they enter a positive integer. Return the integer.
int qPInt(const char question[]) {
int response = qInt(question);
while (response <= 0)
    response = qInt("Try again:");
return response;
}

// Get a character from user. Consumes entire line.
char qChar(const char question[]) {
cout << question << ' ';
const char resp = cin.peek();
cin.ignore(STRING_SIZE, ' ');
return resp;
}

// Return whether cs contains c.
bool contains(const char cs[], const char c) {
for (int i = 0; cs[i]; i++)
    if (cs[i] == c)
      return true;
return false;
}

// Bother user until they select an allowed character.
char qSel(const char question[], const char allowed[]) {
char resp = qChar(question);
while (!contains(allowed, resp))
    resp = qChar("Try again:");
return resp;
}

// Bother the user until they enter y or n. Return true for y, false for n.
bool qYN(const char question[]) { return qSel(question, "yn") == 'y'; }

// Bother the user for a path to a real file. Return the open file.
void qFH(const char question[], ifstream& fh) {
char filename[STRING_SIZE];
qCString(question, filename);
fh.open(filename);
while (!fh.is_open()) {
    qCString("Try again:", filename);
    fh.open(filename);
}
}

// End of reusable stuff ------------------------

struct exerciseData {
char name[STRING_SIZE];
char date[STRING_SIZE];
char note[STRING_SIZE];
int time;
int calories;
int maxHeartRate;
};

// Populate an activity from user input.
void queryActivty(exerciseData &ed) {
qGCString("What exercise activity did you do?", ed.name);
qGCString("What was the date (mm/dd/yy):", ed.date);
ed.time = qPInt("How many minutes?");
ed.calories = qPInt("How many calories did you burn?");
ed.maxHeartRate = qPInt("What was you max heart rate?");
qGCString("Do you have any notes to add?", ed.note);
}

// Populate an activity from a line in a csv.
bool parseActivity(exerciseData &ed, ifstream &fh) {
fh.getline(ed.name, STRING_SIZE, ',');
fh.getline(ed.date, STRING_SIZE, ',');
fh.getline(ed.note, STRING_SIZE, ',');
fh >> ed.time;
fh.ignore();
fh >> ed.calories;
fh.ignore();
fh >> ed.maxHeartRate;
fh.ignore();
}

// Load all the data from a csv. Return number of loaded elements.
int loadData(exerciseData eds[LIST_SIZE]) {
int size = 0;
ifstream fh;
qFH("What is the name of the exercise data text file to load?", fh);
while (true) {
    parseActivity(eds[size], fh);
    if (!fh)
      break;
    size++;
    if (size >= LIST_SIZE)
      break;
}
return size;
}

template <class A, class B, class C, class D, class E, class F>
void showRow(A name, B date, C time, D calories, E maxHeartRate, F note) {
cout << left
       << setw(17) << name
       << setw(10) << date
       << setw(6) << time
       << setw(10) << calories
       << setw(15) << maxHeartRate
       << setw(12) << note
       << ' ';
}

// Search for specific exercise name. Print all matches.
void search(exerciseData eds[], int count) {
char name[STRING_SIZE];
qGCString("What activity would you like to search for?", name);
cout << "Here are the activities matching " << name << ": ";
showRow("Name", "Date", "Time", "Calories", "Max Heartrate", "Note");
for (int i = 0; i < count; i++)
    if (strcmp(name, eds[i].name) == 0)
      showRow(eds[i].name, eds[i].date, eds[i].time, eds[i].calories, eds[i].maxHeartRate, eds[i].note);
}

// Pretty print all the exercises.
void list(exerciseData eds[], int count) {
showRow("Name", "Date", "Time", "Calories", "Max Heartrate", "Note");
for (int i = 0; i < count; i++)
    showRow(eds[i].name, eds[i].date, eds[i].time, eds[i].calories, eds[i].maxHeartRate, eds[i].note);
}

// Ask user to enter an exercise. Increment count if user chooses to save.
void add(exerciseData eds[], int &count) {
if (count >= LIST_SIZE) {
    cout << "You need to stop exercising. ";
} else {
    queryActivty(eds[count]);
    if (qYN("Record the activity time and calories (y/n)?")) {
      count++;
      cout << "Your activity info has been recorded. ";
    }
}
}

int main() {
exerciseData eds[LIST_SIZE];
int size;

cout << "Welcome to the exercise tracking program. ";
size = loadData(eds);

while (true) {
    const char s = qSel("What would you like to do: (l)ist all, (s)earch by"
                        " name, (a)dd an exercise, or (q)uit? :", "lsaq");
    if (s == 'l') {
      list(eds, size);
    } else if (s == 's') {
      search(eds, size);
    } else if (s == 'a') {
      add(eds, size);
    } else if (s == 'q') {
      break;
    }
};

cout << "Thank you for using the exercise tracking program. ";

return 0;
}

------------------------------------------------------------------------------
common.cpp
------------------------------------------------------------
#include "common.h"

// Return true if any char in cs satisfies condition.
// condition has type 'int (*condition)(int)' to match <cstring> convention.
bool any(const char cs[], int (*condition)(int)) {
for (int i = 0; cs[i]; i++)
    if (condition(cs[i]))
      return true;
return false;
}

// The q prefix indicates a function that queries the user.

// Ask the user a question. Dump the response into answer.
void qCString(const char question[], char answer[],
          const int ss = strSize) {
cout << question << ' ';
cin.getline(answer, ss);
}

// Bother the user until they enter a string containing graphical characters.
void qGCString(const char question[], char answer[],
           const int ss = strSize) {
qCString(question, answer, ss);
while (!any(answer, isgraph))
    qCString("Try again:", answer, ss);
}

// Bother the user until they enter a valid integer. Return the integer.
int qInt(const char question[]) {
int resp;
bool fail;
cout << question << ' ';
while (true) {
    cin >> resp;
    fail = cin.fail();
    cin.clear();
    cin.ignore(strSize, ' ');
    if (!fail)
      break;
    cout << "Try again: ";
}
return resp;
}

// Bother the user until they enter a positive integer. Return the integer.
int qPInt(const char question[]) {
int response = qInt(question);
while (response <= 0)
    response = qInt("Try again:");
return response;
}

// Get a character from user. Consumes entire line.
char qChar(const char question[]) {
cout << question << ' ';
const char resp = cin.peek();
cin.ignore(strSize, ' ');
return resp;
}

// Return whether cs contains c.
bool contains(const char cs[], const char c) {
for (int i = 0; cs[i]; i++)
    if (cs[i] == c)
      return true;
return false;
}

// Bother user until they select an allowed character.
char qSel(const char question[], const char allowed[]) {
char resp = qChar(question);
while (!contains(allowed, resp))
    resp = qChar("Try again:");
return resp;
}

// Bother the user until they enter y or n. Return true for y, false for n.
bool qYN(const char question[]) { return qSel(question, "yn") == 'y'; }

// Bother the user for a path to a real file. Return the open file.
void qFH(const char question[], ifstream& fh) {
char filename[strSize];
qCString(question, filename);
fh.open(filename);
while (!fh.is_open()) {
    qCString("Try again:", filename);
    fh.open(filename);
}
}
-------------------------------------------------------------------------------
common.h
------------------------------------------
#include <cstring>
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;

const int strSize = 256;
const int arraySize = 128;

// Return true if any char in cs satisfies condition.
// condition has type 'int (*condition)(int)' to match <cstring> convention.
bool any(const char cs[], int (*condition)(int));

// The q prefix indicates a function that queries the user.

// Ask the user a question. Dump the response into answer.
void qCString(const char question[], char answer[],
          const int ss = strSize);

// Bother the user until they enter a string containing graphical characters.
void qGCString(const char question[], char answer[],
           const int ss = strSize);

// Bother the user until they enter a valid integer. Return the integer.
int qInt(const char question[]);

// Bother the user until they enter a positive integer. Return the integer.
int qPInt(const char question[]);

// Get a character from user. Consumes entire line.
char qChar(const char question[]);

// Return whether cs contains c.
bool contains(const char cs[], const char c);

// Bother user until they select an allowed character.
char qSel(const char question[], const char allowed[]);

// Bother the user until they enter y or n. Return true for y, false for n.
bool qYN(const char question[]);

// Bother the user for a path to a real file. Return the open file.
void qFH(const char question[], ifstream& fh);
-------------------------------------------------------------------------------
exerciseJournal.cpp
---------------------------------------------------------
#include "exerciseJournal.h"

// struct exerciseData {
//   char name[strSize];
//   char date[strSize];
//   char note[strSize];
//   int time;
//   int calories;
//   int maxHeartRate;
// };

// exerciseData eds[arraySize];
// int countAndIndex;
// char fileName[strSize];

// Populate an activity from a line in a csv.
bool parseActivity(exerciseData &ed, ifstream &fh) {
fh.getline(ed.name, strSize, ',');
fh.getline(ed.date, strSize, ',');
fh.getline(ed.note, strSize, ',');
fh >> ed.time;
fh.ignore();
fh >> ed.calories;
fh.ignore();
fh >> ed.maxHeartRate;
fh.ignore();
}

template <class A, class B, class C, class D, class E, class F>
void showRow(A name, B date, C time, D calories, E maxHeartRate, F note) {
cout << left
       << setw(17) << name
       << setw(10) << date
       << setw(6) << time
       << setw(10) << calories
       << setw(15) << maxHeartRate
       << setw(12) << note
       << ' ';
}

// Populate an activity from user input.
void queryActivty(exerciseData &ed) {
qGCString("What exercise activity did you do?", ed.name);
qGCString("What was the date (mm/dd/yy):", ed.date);
ed.time = qPInt("How many minutes?");
ed.calories = qPInt("How many calories did you burn?");
ed.maxHeartRate = qPInt("What was you max heart rate?");
qGCString("Do you have any notes to add?", ed.note);
}

// Load all the data from a csv. Return number of loaded elements.
int exerciseJournal::loadData() {
int countAndIndex = 0;
ifstream fh(fileName);
while (true) {
    parseActivity(eds[countAndIndex], fh);
    if (!fh)
      break;
    countAndIndex++;
    if (countAndIndex >= arraySize)
      break;
}
return size;
}

void exerciseJournal::writeData();

// Ask user to enter an exercise. Increment count if user chooses to save.
void exerciseJournal::add() {
if (countAndIndex >= arraySize) {
    cout << "You need to stop exercising. ";
} else {
    queryActivty(eds[countAndIndex]);
    if (qYN("Record the activity time and calories (y/n)?")) {
      countAndIndex++;
      cout << "Your activity info has been recorded. ";
    }
}
}

// Search for specific exercise name. Print all matches.
bool exerciseJournal::search() {
char name[strSize];
qGCString("What activity would you like to search for?", name);
cout << "Here are the activities matching " << name << ": ";
showRow("Name", "Date", "Time", "Calories", "Max Heartrate", "Note");
for (int i = 0; i < countAndIndex; i++)
    if (strcmp(name, eds[i].name) == 0)
      showRow(eds[i].name, eds[i].date, eds[i].time,
          eds[i].calories, eds[i].maxHeartRate, eds[i].note);
}

// Pretty print all the exercises.
void exerciseJournal::listAll() {
showRow("Name", "Date", "Time", "Calories", "Max Heartrate", "Note");
for (int i = 0; i < countAndIndex; i++)
    showRow(eds[i].name, eds[i].date, eds[i].time,
        eds[i].calories, eds[i].maxHeartRate, eds[i].note);
}
---------------------------------------------------------------------------
exerciseJournal.h
----------------------------------
#include <cstring>
#include <iostream>
#include <fstream>
using namespace std;

const int strSize = 128;
const int arraySize = 100;

struct exerciseData {
char name[strSize];
char date[strSize];
char note[strSize];
int time;
int calories;
int maxHeartRate;
};

class exerciseJournal {
exerciseData eds[arraySize];
int countAndIndex;
char fileName[strSize];
public:
int loadData();
void writeData();
void add();
bool search();
void listAll();
};
-------------------------------------------------
exercise.txt
--------------
Elliptical,06/10/17,great workout,40,400,135
Treadmill,06/12/17,doggin it,20,150,120
Stationary Bike,06/15/17,felt good,30,200,130
Elliptical,06/20/17,great-worked out with Mike,45,350,140

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote