Home > Net >  getting N values from getline(cin) for different data types
getting N values from getline(cin) for different data types

Time:07-16

I have created a program, which requires user to define at least 2 arguments(3rd is optional). Arguments are:

  1. Command(string type)
  2. Date(custom type/class)
  3. Event(string type) - Optional.

I have come up with the following idea:

int main() {
    Database db /*my custom class */
    string command;
    vector<string> arguments;
    while (getline(cin, command, ' ')) {
        arguments.push_back(command);
    }

and after that the idea was to use indexing for arguments:

if(arguments[0] == "Add"){
        Date date arguments[1];
        db.Add(date,arguments[2])
}

} else if(arguments[0] == "Find"){
        Date date = arguments[1];
        db.Find(date);

But unfortunately, Date date = arguments[1]; doesn't work. How can I handle a problem of user input arguments with different data types, with one argument optional.

Here is my Date class:

class Date {
public:
    Date(){
        

    };
    Date(int new_year, int new_month, int new_day){
        if(new_month > 12 || new_month < 1){
            throw invalid_argument("Month value is invalid: "   to_string(new_month));
        } else if (new_day > 31 || new_day < 1){
            throw invalid_argument("Day value is invalid: "   to_string(new_day));
        }
        year = new_year;
        month = new_month;
        day = new_day;

    }
    int GetYear() const{
        return year;
    };
    int GetMonth() const{
        return month;
    };
    int GetDay() const{
        return day;
    };
private:
    int year;
    int month;
    int day;
};


bool operator<(const Date& lhs, const Date& rhs){
    if(lhs.GetYear() != rhs.GetYear()){
        return (lhs.GetYear() - rhs.GetYear()) < 0;
    } else if(lhs.GetMonth() != rhs.GetMonth()){
        return (lhs.GetMonth() - rhs.GetMonth()) < 0;
    } else {
        return (lhs.GetDay() - rhs.GetDay()) < 0;
    }
};


bool operator>(const Date& lhs, const Date& rhs){
    if(lhs.GetYear() != rhs.GetYear()){
        return (lhs.GetYear() - rhs.GetYear()) > 0;
    } else if(lhs.GetMonth() != rhs.GetMonth()){
        return (lhs.GetMonth() - rhs.GetMonth()) > 0;
    } else {
        return (lhs.GetDay() - rhs.GetDay()) > 0;
    }
};


bool operator==(const Date& lhs, const Date& rhs){
    if(lhs.GetYear() == rhs.GetYear() && lhs.GetMonth() == rhs.GetMonth() && lhs.GetDay() == rhs.GetDay()){
        return true;
        }
    return false;
};

ostream& operator<<(ostream& stream, const Date& date){
    stream << date.GetYear() << '-' << date.GetMonth() << '-' << date.GetDay();
    return stream;
}

istream& operator>>(istream& stream, Date& d) { //возвращать будем ссылку на поток
    //if (stream) return stream;
    int year,month,day;
    char c;

    stream >> year >> c >> month >> c >> day;

    if (stream && c == '-')
        d = Date(year,month,day);

    return (stream);
}

CodePudding user response:

But unfortunately, Date date = arguments[1]; doesn't work.

Correct, because arguments[1] is a std::string, but Date does not have a constructor that accepts a std::string. So add one, eg:

class Date {
public:
    Date() {
        setDate(0, 1, 1);
    };

    Date(int new_year, int new_month, int new_day) {
        setDate(new_year, new_month, new_day);
    }

    Date(const string &str) {
        int new_year, new_month, new_day;
        // parse str to extract new_year, new_month, new_day as needed...
        setDate(new_year, new_month, new_day);
    }

    void setDate(int new_year, int new_month, int new_day) {
        if(new_month > 12 || new_month < 1){
            throw invalid_argument("Month value is invalid: "   to_string(new_month));
        } else if (new_day > 31 || new_day < 1){
            throw invalid_argument("Day value is invalid: "   to_string(new_day));
        }
        year = new_year;
        month = new_month;
        day = new_day;
    }
    ...
};

There are many ways to parse a string to extract sub values. For instance, you can use:

  • string::find() string::substr() std::stoi()
  • std::istringstream operator>>
  • std::sscanf()
  • std::regex
  • etc

Use whatever you are most comfortable with. Just for demonstration purposes, let's use std::sscanf():

#include <cstdio>

class Date {
public:
    ...

    Date(const string &str) {
        int new_year, new_month, new_day;
        if ((sscanf(str.c_str(), "M----", &new_year, &new_month, &new_day) != 3) &&
            (sscanf(str.c_str(), "M/-/-", &new_year, &new_month, &new_day) != 3)) {
           throw invalid_argument("Str value is invalid: "   str);
        }
        setDate(new_year, new_month, new_day);
    }

    ...
};

On a side note, you might consider using command-line arguments instead of input I/O, eg:

int main(int argc, const char* argv[]) {
    ...
    vector<string> arguments;
    for (int i = 1; i < argc;   i) {
        arguments.push_back(argv[i]);
    }
    ...
}

And then users can call your program like this:

program.exe command date event

Instead of running program.exe first and then entering command, date, and event separately.

CodePudding user response:

Date date = <whatever>
calls a constructor (like Date date(<whatever>); ). I do not see a constructor for Date that takes a string.

This notation is only available for constructors that have one argument. Generally = is an assignment operator call, or sometimes a constructor call (namely, when you declare a variable like in Date date = ...; as opposed to Date date; date = ...; – this would be an assignment operator call).

You can write Date date(int1, int2, int3); where the ints are obviously some ints retrieved from the input. You have to either give it 3 ints (as you have in the constructor) – or else, to make the main() code simpler, write a constructor taking a string and retrieve ints from the string.

  •  Tags:  
  • c
  • Related