Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

std::ostringstream & LC_NUMERIC

18 views
Skip to first unread message

mathieu

unread,
Oct 7, 2008, 8:49:07 AM10/7/08
to
Hi,

I am playing with the following C++ piece of code (*). At least on
my system debian/gcc 4.3 it looks like I am not writing out a floating
point separator as a comma. what are the operation affected by the
LC_NUMERIC env var value ?

Thanks
-Mathieu


(*)
#include <sstream>
#include <iostream>
#include <stdlib.h>

int main(int argc, char *argv[])
{
setenv("LC_NUMERIC", "fr_FR", 1);
std::ostringstream os;
double d = 1.2;
os << d;
std::string s = os.str();
std::cout << s << std::endl;
std::string::size_type pos_comma = s.find( "," );
if( pos_comma != std::string::npos )
{
return 1;
}
std::string::size_type pos_dot = s.find( "." );
if( pos_dot == std::string::npos )
{
return 1;
}
std::cout << "dot found" << std::endl;
return 0;
}

James Kanze

unread,
Oct 8, 2008, 5:00:28 AM10/8/08
to
On Oct 7, 2:49 pm, mathieu <mathieu.malate...@gmail.com> wrote:

> I am playing with the following C++ piece of code (*). At
> least on my system debian/gcc 4.3 it looks like I am not
> writing out a floating point separator as a comma. what are
> the operation affected by the LC_NUMERIC env var value ?

In C++, nothing. All setting an envirionment variable does is
make it available to your process and its sub-processes (at
least under Unix---what it does outside of your process is
really very system dependent).

What you usually do in C++ is just the opposite; you read the
variable to know how to set your locale. While creating a
locale with the name "" (an empty string) is formally
implementation defined, it is a more or less established
convention, at least under Unix, that this locale will depend on
all of the LC_ environment variables. In other words, you will
use the locale the user specified.

> #include <sstream>
> #include <iostream>
> #include <stdlib.h>

> int main(int argc, char *argv[])
> {
> setenv("LC_NUMERIC", "fr_FR", 1);

This could affect creating a locale with the empty string, but
the main reason you would want to do this would be to inform
sub-processes to use the "fr_FR" locale.

> std::ostringstream os;
> double d = 1.2;
> os << d;
> std::string s = os.str();
> std::cout << s << std::endl;
> std::string::size_type pos_comma = s.find( "," );
> if( pos_comma != std::string::npos )
> {
> return 1;
> }
> std::string::size_type pos_dot = s.find( "." );
> if( pos_dot == std::string::npos )
> {
> return 1;
> }
> std::cout << "dot found" << std::endl;
> return 0;
> }

And you've never touched the actual locales used by your
program. In general:

-- A locale (i.e. std::locale) object created with the empty
string as a name will normally correspond to the "locale"
active in whatever process invoked you---under Unix, it will
normally use the LC_ environment variables, and perhaps
LANG, to determine this.

-- There is a global locale, used whenever you do not specify a
locale. On program start up, this is set to the "C" locale
(which might be appropriate for parsing C++ sources, but not
for much else); you can change it by calling
std::locale::global with the locale you want as the global
locale.

Combined with the previous point: almost every program which
generates human output should start with:
std::locale::global( std::locale( "" ) ) ;
Or if you really want to force a French locale, even for
users who don't want it:
std::locale::global( std::locale( "fr_FR" ) ) ;
(assuming the Unix naming conventions for locales.)

-- For any given IO, you can force the locale just for that
stream, by using imbue. Thus, if you've done the above (and
thus don't really know what the global locale is), but want
to output C++ code (for example), you should imbue the file
with the "C" locale; either:
output.imbue( std::locale( "C" ) ) ;
or:
output.imbue( std::locale::classic() ) ;
(which uses a special static member function to get this
very special locale). Similarly, if you want to force
output in the French locale for just this file:
output.imbue( std::locale( "fr_FR" ) ) ;

-- Finally, you can mix locales. If you want the classic
locale in general, but you want numbers formatted according
to the rules in French, you can create a custom locale for
this, e.g.
std::locale( std::locale::classic(),
"fr_FR",
std::locale::numeric )
In otherwords, copy std::locale::classic() (the first
argument), except for the numeric category (the third
argument, which can be an or'ed list of facets), which is
taken from the locale named "fr_FR".

In general, this is fairly tricky, however, and you have to
know what you are doing, and how the different "facets"
interact. (Each category is implemented by one or more
facets.)

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

mathieu

unread,
Oct 8, 2008, 5:25:52 AM10/8/08
to

Thanks ! That's extremely detailed :)

Since I am a just library author I can not rely on the fact that my
user will start their main program with


std::locale::global( std::locale( "" ) ) ;

Instead I'll have to make sure any o*stream are created (within the
lib, or externally by the user) with
os.imbue(std::locale::classic());

Regards
-Mathieu

0 new messages