Thread specific locale manipulation in C++

2019-06-07 02:32发布

问题:

Is there any standard way doing locale setting across platforms per thread? I see that xlocale provides uselocale, but it is not supported in Windows. There is "_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);" in windows, after which setlocale works on per thread basis. My question is, is there a library that provides these locale specific manipulations in a platform independent way??? Or some other way of doing it?

Thanks, Gokul.

回答1:

I've had the same problem and I eventually wrote some small cross-compatibility code for it. I tried to follow the specification as close as possible, but it does have a few limitations.

In this code newlocale does not allow you to specify a base locale and you're also not able to mix the category masks. But for some basic switching between different locales this should be enough.

// Locale Cross-Compatibility
#ifdef _WIN32
#define locale_t         _locale_t
#define freelocale       _free_locale

#define LC_GLOBAL_LOCALE ((locale_t)-1)
#define LC_ALL_MASK      LC_ALL
#define LC_COLLATE_MASK  LC_COLLATE
#define LC_CTYPE_MASK    LC_CTYPE
#define LC_MONETARY_MASK LC_MONETARY
#define LC_NUMERIC_MASK  LC_NUMERIC
#define LC_TIME_MASK     LC_TIME

// Base locale is ignored and mixing of masks is not supported
#define newlocale(mask, locale, base) _create_locale(mask, locale)

locale_t uselocale(locale_t new_locale)
{
    // Retrieve the current per thread locale setting
    bool bIsPerThread = (_configthreadlocale(0) == _ENABLE_PER_THREAD_LOCALE);

    // Retrieve the current thread-specific locale
    locale_t old_locale = bIsPerThread ? _get_current_locale() : LC_GLOBAL_LOCALE;

    if(new_locale == LC_GLOBAL_LOCALE)
    {
        // Restore the global locale
        _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
    }
    else if(new_locale != NULL)
    {
        // Configure the thread to set the locale only for this thread
        _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);

        // Set all locale categories
        for(int i = LC_MIN; i <= LC_MAX; i++)
            setlocale(i, new_locale->locinfo->lc_category[i].locale);
    }

    return old_locale;
}
#endif

Put that code in your project and you'll be able to switch the locale per-thread on any platform like this:

#include <locale.h>
#ifdef __APPLE__
#include <xlocale.h>
#endif

// Apply a new locale to this thread
locale_t locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
locale_t old_locale = uselocale(locale);

// Print out some text
printf("Always use dot-decimal notation: %f", 1.5);

// Restore the global locale
uselocale(old_locale);
freelocale(locale);


回答2:

Boost::Locale provides a locale interface which is cross-platform, based on the iconv library. It might not be your ideal interface if you're hoping for C standard library functions to behave appropriately as soon as you set the locale. You will, instead, manually manage locales as objects, and explicitly use them. You explicitly generate locales and use them to create comparators and whatever else you need that's locale specific.

On the one hand, this means any previously developed code you wrote which relies on locale being specific will have to be rewritten. On the other hand, there are many fewer gotchas hidden under the hood, as you're explicitly handling what language/encoding is used where.