5
\$\begingroup\$

The program outputs a menu asking for a new question or to ask a question. If you ask a question, it will search a .tsv text file for the question and return the answer. It will then check a map that is used for time and date, if it exists, it will execute the function intended. If not, it will just print the answer. As for a new question, it simply edits the file and inputs the question then answer. I'd like to expand this program to be bigger in the future, multiple files, maps, and better deduction on questions.

So far, this is what I have: 5 files, a main, time and date header and cpp, and file header and .cpp.

getTime.h

#ifndef H_getTime
#define H_getTime

#include <time.h>
#include <string>

using namespace std;

class getTime {
public:
    void printCurrentTime();
    void printCurrentDate();
private:
    string currentTime;
    string currentDate;
    string strMonth;
    int hours;
    int minutes;
    int seconds;
    int day;
    int month;
    int year;
    time_t now;
    struct tm *current;
};

getTime.cpp

#include "getTime.h"
#include <iostream>

using namespace std;

void getTime::printCurrentTime() {
    now = time(0);
    current = localtime(&now);
    hours = current->tm_hour;
    minutes = current->tm_min;
    seconds = current->tm_sec;

    currentTime = to_string(hours) + ":" + to_string(minutes) + ":" + to_string(seconds);
    cout << "The current time is: " << currentTime << endl;
}

void getTime::printCurrentDate() {
    now = time(0)
    current = localtime(&now);
    day = current->tm_mday;
    month = current->tm_mon;
    year = current->tm_year;

    switch (month) {
    case 0:
        strMonth = "Jan";
        break;
    case 1:
        strMonth = "Feb";
        break;
    case 2:
        strMonth = "Mar";
        break;
    case 3:
        strMonth = "April";
        break;
    case 4:
        strMonth = "May";
        break;
    case 5:
        strMonth = "June";
        break;
    case 6:
        strMonth = "July";
        break;
    case 7:
        strMonth = "Aug";
        break;
    case 8:
        strMonth = "Sep";
        break;
    case 9:
        strMonth = "Oct";
        break;
    case 10:
        strMonth = "Nov";
        break;
    case 11:
        strMonth = "Dec";
        break;
    }

    currentDate = to_string(day) + " " + strMonth + " " + to_string(1900 + year);

    cout << "The current date is: " << currentDate << endl;
}

fileRW.h

#ifndef H_fileRW
#define H_fileRW

#include <cstdio>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

class fileRW {
public:
    string readFile(string q);
    void writeFile(string q, string a);
    fileRW();
private:
    string inFile;
    FILE* fout;
    ifstream fin;
    string temp1, temp2;
    string input;
    bool error;
    size_t pos;
};

#endif

fileRW.cpp

#include "fileRW.h"

using namespace std;

fileRW::fileRW() {
    inFile = "C:\\Path\\To\\TSV\\Text\\File.txt";
}
void fileRW::writeFile(string q, string a) {
    string outString = q + "\t" + a;
    fout = fopen(inFile.c_str(), "a+b");
    fprintf(fout, "%s", outString.c_str());
    fprintf(fout, "\n");
    fclose(fout);
}
string fileRW::readFile(string q) {
    fin.open(inFile);
    error = false;
    while (!fin.eof()) {
        getline(fin, input);
        if (!fin) break;
        pos = input.find('\t');
        if (string::npos == pos) {
            break;
        }
        temp1 = input.substr(0, pos);
        temp2 = input.substr(pos + 1, input.length() - pos);
        if (q == temp1) {
            fin.close()
            return temp2;
        }
    }
    if (error) {
        cout << "File is corrupted..." << endl;
    }
    fin.close()
    return "Question not found";
}

main.cpp

#include "getTime.h"
#include "fileRW.h"
#include <iostream>
#include <map>
#include <string>

using namespace std;

typedef string KeyType;
typedef void(getTime::*DoFunc)(void);
typedef pair<const KeyType, DoFunc> Pair;
typedef map<KeyType, DoFunc> mapTimeCall;

int nReadLine();
bool findA(string, mapTimeCall);

int main() {
    string input;
    string q, a;
    char endLoop = 'N';
    int menu = 0;
    fileRW frw;
    mapTimeCall callTimeMap;

    callTimeMap.insert(Pair("printCurrentTime()", &getTime::printCurrentTime));
    callTimeMap.insert(Pair("printCurrentDate()", &getTime::printCurrentDate));

    while (endLoop != 'Y') {
        cout << "1. New Question" << endl;
        cout << "2. Ask Question" << endl;
        cout << "3. Exit" << endl;
        cout << "Enter command: ";
        menu = nReadLine();
        cout << endl;

        switch (menu) {
        case 1:
            getline(cin, q);
            cout << "What is the question: ";
            getline(cin, q);
            cout << endl << "And the answer: ";
            getline(cin, a);
            cout << endl;
            frw.writeFile(q, a);
            break;
        case 2:
            getline(cin, q);
            cout << "What is the question: ";
            getline(cin, q);
            cout << endl;
            a = frw.readFile(q);
            if (!(findA(a, callTimeMap))) {
                cout << "The answer is: " << a << endl;
            }
            break;
        case 3:
           endLoop = 'Y';
           break;
        }
    }

    system("pause");
    return 0;
}
int nReadLine() {
    int input;
    cin >> input;
    return input;
}
bool findA(string a, mapTimeCall timeCall) {
    getTime getTime;
    mapTimeCall::iterator x;
    x = timeCall.find(a);
    if (x != timeCall.end()) {
        DoFunc f = x->second;
        (getTime.*f)();
        return true;
    }
    return false;
}

To have an example of this working, follow these steps when program is loaded:

  • Type: 1
  • Press: Enter
  • Type: What time is it?
  • Press: Enter
  • Type: printCurrentTime()
  • Press: Enter
  • Type: 1
  • Press: Enter
  • Type: What is the date?
  • Press: Enter
  • Type: printCurrentDate()
  • Press: Enter
  • Type: 1
  • Press: Enter
  • Type: What is your name?
  • Press: Enter
  • Type: (type some name)
  • Press: Enter
  • Type: 2
  • Press: Enter
  • Type: (type previous entered question)
  • Press: Enter Output Should be an answer that you typed in.

OR, create a text file and fill in the format as question "TAB Answer Newline"

\$\endgroup\$
2
\$\begingroup\$

A few words on coding style:

Naming of user defined types in C++: The naming convention you've used for the types getTime and fileRW is not very usual for C++ code. Usually, user defined names use CamelCase, with the first letter upper-case. Names using camelCase with the first in lower-case, are more commonly preferred for variable and method names.

But also, getTime is a bad name for a type, as it reads like a command. The class seems more like a timer or clock, so it should have a name that reflects that.

using namespace std in a header file is really a bad idea. You should definitely change that. In the .cpp file it is fine. But once you remove it from the header you might also consider removing it from the source files as well to be more consistent. It is not much more typing adding the std:: prefix to stuff. Your editor will certainly have auto-complete as well.

Switch vs an array:

There is a very big switch statement inside printCurrentDate() which is sequential, with the cases going from 0 to 11, for each month of the year. This would look so much better as an array of strings:

static const char * months[] = {
    "Jan",
    "Feb",
    "Mar",
    "April",
    "May",
    "June",
    "July",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec"
};

// shield yourself from bad inputs with an assertion
assert(month >= 0 && month < 12);
const char * monthName = months[month];

Note: Or use std::string instead of char*. String is better if you need to manipulate the text, as it provides several methods for that.

fileRW weirdness:

fileRW keeps two different types of file interfaces as member data, a C FILE * and a C++ ifstream. Why would you want all that ununiformity? What is the problem with using the ifstream for writing and reading a file?

Also, there seem to be no reason at all for the file handlers to be members of fileRW, as the files are opened and closed inside the method.

But taking a closer look, fileRW shouldn't even exit. It is clearly just a pair of "pure" functions: readFile() and writeFile(). You don't need to make everything an object just because you are using a language that fosters OOP. C++ is not a single paradigm language. A lot of problems are better solved with simple functions.

Use more references:

I suspect some of your string parameters should be const references. The default in C++ is a copy, so it can get inefficient quite easily if you overlook this aspect. As a rule of thumb, when passing a complex object to a function, if the function is only going to read from this object and not make copies of it, then the object should be passed by const reference (const T&).

\$\endgroup\$
  • \$\begingroup\$ Thank you for your input. On the read/writing, i used the C FILE * because i heard it was faster than C++ ifstream. Though, for consistency and clarity, i should use one or the other. \$\endgroup\$ – CodeMonkey Oct 8 '14 at 13:18
  • \$\begingroup\$ @CodeMonkey, the C++ streams are wrapper classes for the C FILE interface. The streams are all template classes, so they get inlined by the compiler. The performance difference is only marginal. You should use the one that is more convenient to you and that makes the code clearer. \$\endgroup\$ – glampert Oct 8 '14 at 16:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.