When std::locale::global() is set to a custom C++ locale with no name (e.g., "*"), the underlying C locale is not updated via std::setlocale(). This causes boost::lexical_cast to use different locales for output and input:
double → string uses the C locale (snprintf) — formatting with '.'
string → double uses the C++ locale (std::stringstream) — expecting '#' (or whatever the custom facet defines)
Example:
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <locale>
#include <clocale>
#include <stdexcept>
// Custom facet: uses '#' as decimal point separator
class HashDecimalPoint:
public std::numpunct<char>
{
protected:
char do_decimal_point() const override
{
return '#';
}
};
int main()
{
try
{
// 1. Create C++ locale with '#' as decimal separator
std::locale hashLocale(std::locale::classic(), new HashDecimalPoint());
std::cout << "hashLocale name: \"" << hashLocale.name() << "\"" << std::endl;
// 2. Set global C locale to "C" (uses '.' as decimal separator)
if(!std::setlocale(LC_ALL, "C"))
{
std::cerr << "Failed to set C locale" << std::endl;
return 1;
}
std::cout << "C locale set to: " << std::setlocale(LC_ALL, nullptr) << std::endl;
// 3. Set global C++ locale to hashLocale
// IMPORTANT: hashLocale.name() == "*", so std::locale::global() does NOT call setlocale()
std::locale::global(hashLocale);
std::cout << "C++ global locale set to hashLocale (name: \""
<< std::locale().name() << "\")" << std::endl;
// Check current locales
std::cout << "\nCurrent locales:" << std::endl;
std::cout << " C locale: " << std::setlocale(LC_ALL, nullptr) << std::endl;
std::cout << " C++ locale: " << std::locale().name() << std::endl;
const double value = 23.9;
// 4. double -> string conversion (uses C locale internally via snprintf)
std::string numToStr = boost::lexical_cast < std::string > (value);
std::cout << "\ndouble -> string: " << value << " -> \"" << numToStr << "\"" << std::endl;
std::cout << " Contains '.': " << (numToStr.find('.') != std::string::npos ? "yes" : "no") << std::endl;
std::cout << " Contains '#': " << (numToStr.find('#') != std::string::npos ? "yes" : "no") << std::endl;
// 5. string -> double conversion (uses C++ locale via stringstream)
// BUG: expects '#' (C++ locale) but string contains '.' (from C locale)
std::cout << "\nstring -> double: trying to parse \"" << numToStr << "\"" << std::endl;
try
{
double result = boost::lexical_cast<double>(numToStr);
std::cout << " Result: " << result << " (unexpected success)" << std::endl;
}
catch(const boost::bad_lexical_cast& e)
{
std::cout << " BUG DEMONSTRATED: boost::bad_lexical_cast - " << e.what() << std::endl;
std::cout << " Reason: C locale formatted with '.', but C++ locale expects '#'" << std::endl;
}
// 6. Parsing with '#' works when using current C++ locale
std::cout << "\nParsing \"1#5\" with current C++ global locale:" << std::endl;
double hashResult = boost::lexical_cast<double>("1#5");
std::cout << " Result: " << hashResult << " (works - matches C++ locale)" << std::endl;
}
catch(const std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
return 1;
}
return 0;
}
When std::locale::global() is set to a custom C++ locale with no name (e.g., "*"), the underlying C locale is not updated via std::setlocale(). This causes boost::lexical_cast to use different locales for output and input:
double → string uses the C locale (snprintf) — formatting with '.'
string → double uses the C++ locale (std::stringstream) — expecting '#' (or whatever the custom facet defines)
Example: