8

This function is supposed to read a fraction and place it in an array. If the user enters '0' the function is supposed to exit. I am trying to do this using the cin.peek() function but execution always goes into the if statement and doesn't allow the user to exit.

How should I properly code this (I am open to not using peek(), I thought it was the simplest way of doing it.)

Thanks!

void enterFrac(Fraction* fracs[], int& index)
    {
        int n, d;
        char c, slash;
        cout << "Enter fractions (end by entering a 0): ";
        c = cin.peek();

        if ( c != '0')
        {
            cin >> n >> slash >> d;
            Fraction* f = new Fraction();
            f->num = n;
            f->den = d;
            fracs[index] = f;
            index++;
        }
    }

This test of peek() works however:

#include <iostream>
using namespace std;

int main () {
  char c;
  int n;
  char str[256];

  cout << "Enter a number or a word: ";
  c=cin.peek();

  if ( (c >= '0') && (c <= '9') )
  {
    cin >> n;
    cout << "You have entered number " << n << endl;
  }
  else
  {
    cin >> str;
    cout << " You have entered word " << str << endl;
  }

  return 0;
}
Zzz
  • 2,927
  • 5
  • 36
  • 58
  • I added a case where I can get peek() to work correctly, but I don't see what is causing the problem in my case. – Zzz Nov 21 '12 at 21:19

1 Answers1

16

There are two issues with your use of std::istream::peek():

  1. This function access the next character and does not skip leading whitespace. You probably want to skip leading whitespace before determining what the next character is, e.g., using the manipulator std::ws: (std::cin >> std::ws).peek().
  2. The result from std::istream::peek() is not a char. Instead, it is an std::char_traits<char>::int_type (which is a fancy spelling of int). The result may, e.g., be std::char_traits<char>::eof() and if the value of '0' happens to be negative (I'm not aware of any platform where it is; however, e.g., the funny character from my name 'ü' is a negative value on platforms where char is signed) you wouldn't get the correct result, either. That is, you normally compare the result of std::istream::peek() against the result of std::char_traits<char>::to_int_type(), i.e., you'd use something like this: std::cin.peek() == std::char_traits<char>::to_int_type('0')

That said, your program doesn't check whether it could successfully read the nominator and the denominator, separated by a slash. You always want to verify that reading was successful, e.g., using something like

if ((std::cin >> nominator >> slash >> denominator) && slash == '/') {
    ...
}

Just for entertainment, you can create a manipulator for testing that a character is a slash, indeed:

std::istream& slash(std::istream& in) {
    if ((in >> std::ws).peek() != std::char_traits<char>::to_int_type('/')) {
        in.setstate(std::ios_base::failbit);
    }
    return in;
}

This way, you'd encapsulate the test for slash. If you need to use this in multiple places this is quite handy.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • This is really helpful information, but I am still left with the same problem, unless I didn't understand something in your explanation :). When the user enters '0' execution enters the if statement. – Zzz Nov 21 '12 at 21:16
  • I am pasting code (in the question) that works using peek(), but I can't figure out how to apply it in my use case. – Zzz Nov 21 '12 at 21:17
  • Did you add skipping of whitespace, i.e., this little `std::ws` bit? I'd be prepared to bet that the character you see in the test is some sort of space, most likely a newline. – Dietmar Kühl Nov 21 '12 at 21:23
  • 1
    @Azzi I think you're falling into the very common trap of thinking that `cin >> anything` or `cin.peek()` reads the next character you type in. It doesn't, it reads the next unread character. As Dietmar says almost certainly after you typed a number you typed a newline. You read the number but you didn't read the newline, so that newline is what cin.peek() reads, even though you type a zero, the unread newline comes first. Add the whitespace extracter as Dietmar says. – john Nov 21 '12 at 22:11