rampion rampion - 1 year ago 54
C++ Question

operator>> returns failure when I enter a 4-digit number after setting the locale

I'm running into some really weird behaviour when I use

to set the
locale for

// example.cpp
#include <iostream>
#include <iomanip>
#include <locale>
int main(){
# ifdef LOCALE
# endif
long temp;
const bool status = static_cast<bool>(std::cin >> temp);
std::cout << std::boolalpha << status << " " << temp << std::endl;

I can compile and run this code without issue if I don't imbue the current

$ g++ example.cpp -o no-imbue -std=c++1y -stdlib=libc++ -Wall -Wextra -Werror
$ echo 100 | no-imbue
true 100
$ echo 1001 | no-imbue
true 1001

However, if I imbue the current locale,
std::cin >> temp
starts failing for
four digit numbers:

$ g++ example.cpp -o imbue-empty -DLOCALE='""' -std=c++1y -stdlib=libc++ -Wall -Wextra -Werror
$ echo 100 | imbue-empty
true 100
$ echo 1001 | imbue-empty
false 1001

as the locale name instead of
seems to have the same

$ g++ example.cpp -o imbue-utf8 -DLOCALE='"en_US.UTF-8"' -std=c++1y -stdlib=libc++ -Wall -Wextra -Werror
$ echo 100 | imbue-utf8
true 100
$ echo 1001 | imbue-utf8
false 1001

I'm on OSX using

$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

Is this a bug with the compiler, or am I doing something wrong?

Answer Source

If you input 1,001, your program should print true.

The en_US locale expects a comma between each group of three digits. Because you didn't provide one, std::num_get::get() sets failbit on std::cin. See the link for more detail, but the relevant excerpts are:

Stage 2: character extraction

If the character matches the thousands separator (std::use_facet<std::numpunct<charT>>(str.getloc()).thousands_sep()) and the thousands separation is in use at all std::use_facet<std::numpunct<charT>>(str.getloc()).grouping().length() != 0, then if the decimal point '.' has not yet been accumulated, the position of the character is remembered, but the character is otherwise ignored. If the decimal point has already been accumulated, the character is discarded and Stage 2 terminates.


Stage 3: conversion and storage

After this, digit grouping is checked. if the position of any of the thousands separators discarded in Stage 2 does not match the grouping provided by std::use_facet<std::numpunct<charT>>(str.getloc()).grouping(), std::ios_base::failbit is assigned to err.