I am using a library which calls std::locale:global to save and restore C++ locale settings.
But std::locale::global set C++ locale settings AND C locale settings as explained in this nice user guide: https://stdcxx.apache.org/doc/stdlibug/24-3.html
This code shows the potential issue:
#include <clocale>
#include <iostream>
class C
{
public:
void Open()
{
// Save current locale settings and set standard one
currentLocale = std::locale::global(std::locale::classic());
std::cout << "Open" << std::endl;
}
void Close()
{
// Restore the previous locale settings
std::locale::global(currentLocale);
std::cout << "Close" << std::endl;
}
std::locale currentLocale;
};
int main(int argc, char** argv)
{
std::setlocale(LC_CTYPE, "");
std::cout << "LC_CTYPE = " << std::setlocale(LC_CTYPE, nullptr) << std::endl;
C myObject;
myObject.Open();
myObject.Close();
std::cout << "LC_CTYPE = " << std::setlocale(LC_CTYPE, nullptr) << std::endl;
return 0;
}
It prints:
LC_CTYPE = en_US.UTF-8
Open
Close
LC_CTYPE = C
But calling Open & Close should not modify any C locale setting and we should get:
LC_CTYPE = en_US.UTF-8
Open
Close
LC_CTYPE = en_US.UTF-8
For now I am only concerned by LC_CTYPE setting so I set C++ LC_CTYPE setting equal to C LC_CTYPE setting before calling Open&Close and the issue is fixed:
int main(int argc, char** argv)
{
std::setlocale(LC_CTYPE, "");
std::cout << "LC_CTYPE = " << std::setlocale(LC_CTYPE, nullptr) << std::endl;
C myObject;
// set c++ locale settings equal to C settings to keep C settings
const std::string ctypeValue = std::setlocale(LC_CTYPE, nullptr);
std::locale cppLocale;
std::locale modifiedcppLocale(cppLocale, ctypeValue, std::locale::ctype);
std::locale::global(modifiedcppLocale);
myObject.Open();
myObject.Close();
std::cout << "LC_CTYPE = " << std::setlocale(LC_CTYPE, nullptr) << std::endl;
return 0;
}
So I have general questions about std::locale::global:
1°) Why std::locale::global also set C locale settings ?
2°) Is it ok to store/restore locale settings by calling only std::locale::global ? It seems that we should also store/restore C locale settings by calling std::setlocale
3°) Is there a solution to have always C++ locale settings = C locale settings ?
So there is C++ std::locale and C locale. And they are separate. One is in C++. The other is in C. In C the locale is global. In C++ the locale is only really the default std::locale()
constructor.
And there are locale categories like LC_CTYPE
. Then there is the whole locale LC_ALL
. LC_ALL
has all locale categories together combined into one.
With std::setlocale(LC_CTYPE, "");
you are setting one LC_CTYPE
C locale category.
After setting std::setlocale(LC_CTYPE, "")
the LC_ALL
locale on glibc becomes LC_CTYPE=en_US.UTF-8;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_MONETARY=C;LC_MESSAGES=C;LC_PAPER=C;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=C;LC_IDENTIFICATION=C
, which you can do std::cout << std::setlocale(LC_ALL, nullptr)
to check.
With std::locale::global(std::locale::classic())
you are setting C++ locale and overriding C locale LC_ALL
and you are getting the previous only C++ part of std::locale.
So now you have overridden C locale and stored previous C++ locale. The previous C locale is lost.
Why std::locale::global also set C locale settings ?
Documentation https://en.cppreference.com/w/cpp/locale/locale/global states that, " If loc has a name, also replaces the C locale as if by std::setlocale(LC_ALL, loc.name().c_str());". LC_ALL
is the whole locale at once, setting LC_ALL
overrides all locale categories.
Is it ok to store/restore locale settings by calling only std::locale::global ?
Sure.
It seems that we should also store/restore C locale settings by caling std::setlocale
Yes.
Is there a solution to have always C++ locale settings = C locale settings ?
You can "sync" C++ default std::global
global with C locale.
int main(int argc, char** argv) {
std::setlocale(LC_CTYPE, "");
std::locale::global(std::locale(std::setlocale(LC_ALL, nullptr)));
Overall, it feels odd to only setlocale(LC_CTYPE
set single locale category. Typically, in C programs, main starts with setlocale(LC_ALL, "")
set the locale (i.e. set all locale categories) to user defined locale.
Typically, in C++, you would start main with setting C locale to user defined locale and setting C++ default std::locale
locale to user defined locale, with:
int main(int argc, char** argv) {
std::locale::global(std::locale(""));