[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

std::ostringstream & LC_NUMERIC

mathieu

10/7/2008 12:49:00 PM

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;
}
2 Answers

James Kanze

10/8/2008 9:00:00 AM

0

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.kanze@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

10/8/2008 9:26:00 AM

0

On Oct 8, 11:00 am, James Kanze <james.ka...@gmail.com> wrote:
> 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.)

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