boostorg / boostorg/lexical_cast

Desynchronized C and C++ Global Locales Break boost::lexical_cast Round-Trip

Open
#98 3 comments 0 reactions 0 assignees View on GitHub
help wanted
Dominant language
C++
Stars
37
Forks
63
Avg merge
4d 19h
Merged PRs (30d)
2

Description

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
#include
#include
#include
#include

// Custom facet: uses '#' as decimal point separator
class HashDecimalPoint:
public std::numpunct
{
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(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("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;
}
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reproducing the example using boost/lexical_cast.hpp, std::locale::global(), and std::setlocale(). Trace the conversion paths for double-to-string and string-to-double, then determine how their locale handling should align. Done means the reported round-trip mismatch is addressed and covered by a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.