2

A little bit of background - I'm new to learning C++. I'm working through Accelerated C++ and following things along. In chapter 4, the code I've got doesn't quite work as the book suggests, and I've double-checked all my of code to make sure I'm not mis-typing something from their example (I can't see any errors, anyway).


I have the following struct and two functions:

struct Student {
  std::string        name;
  double             midtermGrade, finalGrade;
  std::vector<double>homework;
};

std::istream& read(std::istream& is, Student& s) {
  is >> s.name >> s.midtermGrade >> s.finalGrade;

  std::cout << std::endl << "Name: " << s.name << std::endl
            << " - Md: " << s.midtermGrade << std::endl
            << " - Fn: " << s.finalGrade << std::endl;

  read_hw(is, s.homework);

  return is;
}

std::istream& read_hw(std::istream& in, std::vector<double>& hw) {
  if (in) {
    hw.clear();

    double h;

    while (in >> h) {
      std::cout << " - Hw: " << h << std::endl;
      hw.push_back(h);
    }

    in.clear();
  }

  return in;
}

In int main() I'm calling that code like this:

Student record;
while (read(std::cin, record)) {

If I compile and run, then paste (or type) in the following input, I get the following output:

// Input
Johnny 70 80 50 Fred 95 90 100 Joe 40 40 50

// Output
Name: Johnny
 - Md: 70
 - Fn: 80
 - Hw: 50

Name: red
 - Md: 95
 - Fn: 90
 - Hw: 100

Name: Joe
 - Md: 40
 - Fn: 40
 - Hw: 50

As you can see, "Fred" has become "red". I've tried this with a whole bunch of names; anything starting with A-F is affected by the same issue, which caused me to suspect it may be a hexadecimal issue. However, "Nick" becomes "k" and "Xander" simply becomes "r".

I'm utterly baffled. Do you have any idea what's going on?


As requested, here's a MCVE:

#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

struct Student {
  std::string        name;
  double             midtermGrade, finalGrade;
  std::vector<double>homework;
};

std::istream& read_hw(std::istream& in, std::vector<double>& hw) {
  if (in) {
    hw.clear();

    double h;

    while (in >> h) {
      std::cout << " - Hw: " << h << std::endl;
      hw.push_back(h);
    }

    in.clear();
  }

  return in;
}

std::istream& read(std::istream& is, Student& s) {
  is >> s.name >> s.midtermGrade >> s.finalGrade;

  std::cout << std::endl << "Name: " << s.name << std::endl
            << " - Md: " << s.midtermGrade << std::endl
            << " - Fn: " << s.finalGrade << std::endl;

  read_hw(is, s.homework);

  return is;
}

int main()
{
  Student record;

  std::vector<Student> students;

  while (read(std::cin, record)) {}

  return 0;
}
Joe
  • 15,669
  • 4
  • 48
  • 83
  • 4
    Can you provide your `main` so that we could independently run and reproduce your issue? – Bartek Banachewicz Jun 07 '17 at 14:22
  • 5
    I can't reproduce. Please provide a [MCVE]. Your example is nearly there. – François Andrieux Jun 07 '17 at 14:22
  • putting [`endl` in the middle of the string is not a good idea](https://stackoverflow.com/q/213907/995714) – phuclv Jun 07 '17 at 14:26
  • 5
    [Works fine here](https://ideone.com/aEVaCy), and also locally on my machine. Tried your newly-added MCVE, and can't repro it with that, either. Something's unusual about your system configuration. What compiler are you using? Which OS? – Cody Gray - on strike Jun 07 '17 at 14:26
  • @CodyGray minimal example added - it's entirely possible that it's something on my machine. I'm on a Mac running Sierra, compiling with G++ for C++11. Beyond that, I don't know what else is relevant so feel free to ask me for information that might be – Joe Jun 07 '17 at 14:27
  • [Still cannot reproduce](http://ideone.com/0uTx1R) – NathanOliver Jun 07 '17 at 14:28
  • @LưuVĩnhPhúc I've swapped out the `endl`s for `\n` and still get the same behaviour – Joe Jun 07 '17 at 14:29
  • @NathanOliver I'm definitely willing to believe it's an issue with my computer rather than the code. See my above comment to Cody, do you have any idea what might cause an issue like this, and is there any info I can provide to help figure it out? – Joe Jun 07 '17 at 14:30
  • 1
    Also works fine here (gcc 6.3.0 on arch linux). What compiler/ide/terminal emulator are you using? Have you tried running the code directly from the terminal? – sknt Jun 07 '17 at 14:32
  • It sounds like you have a bad implementation and when it sees `A-F` it tries to read it in a hex input. When it realizes it can't it does not put the characters back into the stream so you see the first letter truncated. What happens if you change `Fred` to `AAAJoe`? – NathanOliver Jun 07 '17 at 14:32
  • @NathanOliver `AAAJoe` becomes `Joe`, further supporting the hex theory. I don't understand `Xavier` becoming `r` or `Nick` becoming `k` though – Joe Jun 07 '17 at 14:35
  • @Skynet compiler is G++, text editor is Atom and the terminal is the default OSX Terminal. I've tried running directly through that and have the same issue. – Joe Jun 07 '17 at 14:36
  • 1
    If you're on macOS, the compiler is *probably* Clang. It's just lying to you and saying that it's G++. That's a game Apple has been playing for a long time for compatibility reasons. I don't have Sierra here; my Mac is still running Mavericks with an older version of the compiler. (I don't use it for development.) – Cody Gray - on strike Jun 07 '17 at 14:38
  • Eating characters is literally `cin`'s job ;) – Lightness Races in Orbit Jun 07 '17 at 16:07

2 Answers2

4

Having looked into this further, it would appear that libc++ doesn't read doubles from cin in the way you'd except. I'm not sure if it's only specifically doubles, but I think it might be. It only appears to affect the Clang compiler on OSX, and it's really annoying :P

For the TL;DR version please see David's answer which I've accepted.

I'm not currently aware of any good fix for it, but if you add the compiler flag -stdlib=libstdc++ you can force it to use an older, deprecated stdlib which will work as expected. Obviously it has less features (in my example, std::sort and std::domain_error were missing). Other options include not using double and doing further parsing on the input (although as I'm new to C++, I don't have a scooby on that one I'm afraid).

Related reading:

The official bug report was opened 3.5 years ago and still hasn't been resolved.

Joe
  • 15,669
  • 4
  • 48
  • 83
  • It's not a bug. The behavior is correct. The standard specifically says all characters that can be part of the specified type should be consumed. – David Schwartz Jun 08 '17 at 09:49
  • @DavidSchwartz cheers for the extra info - I've updated the answer. At this point of my learning, there's an awful lot of stuff I don't know so thanks for the better explanation of what's actually going on – Joe Jun 08 '17 at 10:05
2

If you have specific requirements for how the decision is to be made whether something is a double or not, you have to write code that implements those requirements. The standard permits any leading characters that could be part of a valid floating point representation to be discarded. Letters like "e", "i", "n" and "f" can be part of a valid floating point representation.

For why this kind of coding is never a good idea, see Falsehoods Programmers Believe About Names, particularly number 15.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278