The C Runtime locale is set by setlocale
.
The Standard C++ Library (STL) locale is set by the std::locale
class and can be set on individual STL objects like std::istringstream etc.
The function _configthreadlocale(_ENABLE_PER_THREAD_LOCALE)
allows setting the C Runtime locale on a per thread basis.
Unfortunately it seems that STL objects in threads where _configthreadlocale(_ENABLE_PER_THREAD_LOCALE)
is enabled is using the C Runtime locale. Or at least using the decimal point of the C Runtime locale.
In threads without _ENABLE_PER_THREAD_LOCALE
there are no problems.
Something similar was asked by 开发者_运维百科Paavo in 2008, but with no answers: _configthreadlocale and localeconv
The following code shows the problem:
//Enable per thread locale in current thread
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)
//Create istringstream object
std::istringstream LibraryStream;
//Create double object
double Value = 0;
//Create std::locale object with "C" locale ("." as decimal point)
std::locale StreamLoc("C");
//Set C Runtime locale to danish ("," as decimal point)
setlocale(LC_ALL, "danish");
//Set the "C" locale on the istringstream object
LibraryStream.imbue(StreamLoc);
//Get the locale of the istringstream object for test (returns "C" as expected)
std::locale NewStreamLoc = LibraryStream.getloc();
//Set floating point string with "C" locale decimal point in istringstream object
LibraryStream.str("60.258351");
//Convert the string to double
LibraryStream >> Value;
//Now the expected value of "Value" is 60.258351, but it is 60.000
//when debugging the conversion, I can see that "," is used as decimal point
Have anyone experienced this before? Am I doing something wrong? Are there any suggestions for solutions?
Thanks in advance /TEB
Since C++ Standard Library only recently gained knowledge of threads, this apparent dichotomy does not surprise me. There is an exhaustive discussion of what happens in different scenarios (Microsoft-specific, but seems to be helpful as general background) here.
In summary:
To change the locale using the C++ Runtime Library, use the locale Class. By calling the locale::global method, you change the locale in every thread that has not explicitly enabled per-thread locale. To change the locale in a single thread or portion of an application, simply create an instance of a locale object in that thread or portion of code.
Disclaimer: I am not an expert at locales, so this may be wrong.
On the Multithreading and Locales Knowlegde Base article it is noted:
Calling locale::global changes the locale for both the Standard C++ Library and the C Runtime Library. However, calling setlocale only changes the locale for the C Runtime Library; the Standard C++ Library is not affected.
You are indeed using the C++ Std Lib, so it seemed to me that you needed to call locale::global()
. When I did this, the return value is as you expected. Here is my sample code:
#include <cstdlib>
#include <locale>
#include <sstream>
using namespace std;
int main()
{
//Enable per thread locale in current thread
int n = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE) ;
//Create istringstream object
std::istringstream LibraryStream;
//Create double object
double Value = 0;
//Create std::locale object with "C" locale ("." as decimal point)
std::locale StreamLoc("C");
//Set C Runtime locale to danish ("," as decimal point)
char* ret = setlocale(LC_ALL, "danish");
//Set the "C" locale on the istringstream object
LibraryStream.imbue(StreamLoc);
locale::global(StreamLoc);
//Get the locale of the istringstream object for test (returns "C" as expected)
std::locale NewStreamLoc = LibraryStream.getloc();
//Set floating point string with "C" locale decimal point in istringstream object
LibraryStream.str("60.258351");
//Convert the string to double
LibraryStream >> Value;
//Now the expected value of "Value" is 60.258351, but it is 60.000
//when debugging the conversion, I can see that "," is used as decimal point
}
Good point John Dibling. I have tested that and the operator>> Value does use the locale in the stream and not the global locale. It was tested by the example below:
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE), removed
locale::global(locale("danish"));, added to set the global Std lib locale
#include <cstdlib>
#include <locale>
#include <sstream>
using namespace std;
int main()
{
//Enable per thread locale in current thread
//int n = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE) ;
//Create istringstream object
std::istringstream LibraryStream;
//Create double object
double Value = 0;
//Create std::locale object with "C" locale ("." as decimal point)
std::locale StreamLoc("C");
//Set C Runtime locale to danish ("," as decimal point)
char* ret = setlocale(LC_ALL, "danish");
//Set Std lib global locale to "danish"
locale::global(locale("danish"));
//Set the "C" locale on the istringstream object
LibraryStream.imbue(StreamLoc);
//Get the locale of the istringstream object for test (returns "C" as expected)
std::locale NewStreamLoc = LibraryStream.getloc();
//Set floating point string with "C" locale decimal point in istringstream object
LibraryStream.str("60.258351");
//Convert the string to double
LibraryStream >> Value;
//In this case the value of "Value" is 60.258351, as expected and thus
//the "C" locale was use for conversion
}
I guess the conclusion so far is that STL objects in threads where _configthreadlocale(_ENABLE_PER_THREAD_LOCALE) is enabled is indeed using the C Runtime locale.
It is possible to work around this by setting both the Std lib locale and the C Runtime locale with the locale::global() call. But as Std lib locale is not thread safe and not affected by the _ENABLE_PER_THREAD_LOCALE, we can still run in to multithreading problems with this workaround.
精彩评论