1

I've been stuck on this homework assignment all week. Just when I get the program to finally run, I realize that by using just cin >> breed, if my input has a space it ruins the code (as my program requires gathering 3 separate variables, first an int, then a string, and last a bool). Because this is the second variable, it messes up my code using phrases that have a white character. When I try changing it to cin.get or cin.getline, this is the error message I get:

c2664 error "cannot convert argument 1 from std::string to _Elem *"

Below is the code in question (the middle line is giving the error). Any help would be greatly appreciated!

#include <iostream>
#include <string>
using namespace std;

int main()
{
    int birthyear;  
    string breed;    
    bool vaccines;  

    cout << "Please enter value for dog's birth year: ";
    cin >> birthyear;
    cout << "What is the breed of the dog: ";
    cin.getline(breed, 100);
    cin.ignore();
    cout << "Has the dog been vaccinated (1 = Yes/ 0 = No): ";
    cin >> vaccines;
}
Bulat
  • 710
  • 7
  • 15
gaara
  • 13
  • 4
  • 3
    Create a [mcve] – eerorika Jun 13 '19 at 01:40
  • 1
    `std::getline(cin, breed);` may be helpful. https://en.cppreference.com/w/cpp/string/basic_string/getline – Eljay Jun 13 '19 at 01:46
  • sorry folks, I'm new to coding and new here. I tried to simplify the code from the total program, and reposted it above. still gets the same error message, even without the classes and functions. – gaara Jun 13 '19 at 01:59
  • Ok, what Eljay suggested eliminated the error message... but if the input has a space or white character in it, it still skips the next lines of code. I thought the point of getline was for that not to happen? – gaara Jun 13 '19 at 02:03
  • `ignore` belongs after instructions that leave a data in the in the stream that you want gone.don't place them before other instructions or randomly throughout the code just in case.. – user4581301 Jun 13 '19 at 03:02

1 Answers1

1

First up, you need to be aware that there are two getline things in C++, one in the I/O area and one in the top-level standard namespace.

cin.getline(breed, 100) is the one in the I/O area (specifically istream::getline() and it knows nothing about strings, preferring to work on character arrays. You should probably avoid that one.

The one that does know about strings is std::getline() and that's generally the preferred one if you don't want to go back to the bad old days of C-legacy "strings".

In addition, you need to be careful in C++ when you mix the type-specific input (like <<) and line-specific input (like getline) operations. It's important to know where the file pointer is before and after each operation.

For example, cin << someInt will leave the file pointer immediately after the integer it reads in. That means, if your next operation is getline(), it's likely to find everything on the line after that integer (at the bare minimum, this will be the newline character that you entered to get the integer processed), not the next line where you're going to be typing in your string.

A simple fix for your case is to ignore everything up to and including the newline before you attempt to get the next line. That can be done with ignore():

#include <iostream>
#include <string>
#include <limits>
using namespace std;

int main() {
    int birthyear; string breed; bool vaccines;

    cout << "Please enter value for dog's birth year: ";
    cin >> birthyear;

    cout << "What is the breed of the dog: ";
    cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
    getline(cin, breed);

    cout << "Has the dog been vaccinated (1 = Yes/ 0 = No): ";
    cin >> vaccines;

    // Output what you got.

    cout << birthyear << " '" << breed << "' " << vaccines << '\n';
}

You could also opt for ensuring all input is line-based (converting those lines to the correct type once they're entered) since that's likely to ease your task of ensuring the pointers are in the right place, and that errors in input (like entering xyzzy for an integer) can be better handled.

Something like this should be a good start:

#include <iostream>
#include <string>
#include <limits>
#include <set>
#include <cstdlib>
using namespace std;

// Get string, always valid. Optionally strip leading and
// trailing white-space.

bool getResp(const string &prompt, string &val, bool strip = false) {
    cout << prompt;
    getline(cin, val);
    if (strip) {
        val.erase(0, val.find_first_not_of(" \t"));
        val.erase(val.find_last_not_of(" \t") + 1);
    }
    return true;
}

// Get unsigned, must ONLY have digits (other than
// leading or trailing space).

bool getResp(const string &prompt, unsigned long &val) {
    string str;
    if (! getResp(prompt, str, true)) return false;

    for (const char &ch: str)
        if (! isdigit(ch)) return false;

    val = strtoul(str.c_str(), nullptr, 10);
    return true;
}

// Get truth value (ignoring leading/trailing space),
// and allow multiple languages.

bool getResp(const string &prompt, bool &val) {
    string str;
    if (! getResp(prompt, str, true)) return false;

    const set<string> yes = {"yes", "y", "1", "si"};
    const set<string> no = {"no", "n", "0", "nyet"};

    if (yes.find(str) != yes.end()) {
        val = true;
        return true;
    }

    if (no.find(str) != no.end()) {
        val = false;
        return true;
    }

    return false;
}

// Test driver for your situation.

int main() {
    unsigned long birthYear;
    std::string dogBreed;
    bool isVaccinated;

    if (! getResp("What year was the dog born? ", birthYear)) {
        std::cout << "** ERROR, invalid value\n";
        return 1;
    }

    if (! getResp("What is the breed of the dog? ", dogBreed, true)) {
        std::cout << "** ERROR, invalid value\n";
        return 1;
    }

    if (! getResp("Has the dog been vaccinated? ", isVaccinated)) {
        std::cout << "** ERROR, invalid value\n";
        return 1;
    }

    std::cout
        << birthYear
        << " '" << dogBreed << "' "
        << (isVaccinated ? "yes" : "no") << '\n';
}
paxdiablo
  • 772,407
  • 210
  • 1,477
  • 1,841
  • How would you go about converting each of the inputs to a line based variable, and converting them after? That sounds helpful but I’m still learning c++ and don’t quite understand that concept? – gaara Jun 13 '19 at 03:56
  • @gaara, see the update. I know you're still learning and there may be a bit there to digest, but it'll be worth your time. – paxdiablo Jun 13 '19 at 04:49
  • Thank you so much! I really appreciate the help and the info. I've taken some of your code so far and it's helped me make a working program. That said, Can you help me to understand the purpose of `getResp`? How is it different/better than `cin`, as I haven't encountered it yet? I really like how your code has allowed for `bool` to accept more than just 1 and 0 as valid answers. Are there other ways to accomplish that same task with a `bool` variable? Thank you again for all of your help! – gaara Jun 14 '19 at 15:38
  • @gaara, you're unlikely to have encountered it since I only *just* wrote it :-) The reason for `getResp` is simply to provide and overloaded function which does a lot of the heavy lifting for you (output a prompt, get the input string and do any checking as required on what was input). This frees your "main" code to simply specify its intent rather than having a lot of conversion and/or checking code in it. – paxdiablo Jun 15 '19 at 07:23