C++ boost crashes while using locale

2019-02-17 15:03发布

I am trying to use boost library for providing i18 support to my string class. I am using Microsoft Visual studio compiler VC10 and 64 bit Windows 7 machine.

I am able to compile and link my application with the boost library however my application crashes while invoking boost::locale::to_upper().

Following is the code I wrote.

#include <boost/locale.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/system/config.hpp>



 String::MakeUpper()()
    {
    boost::locale::generator gen;
    std::locale loc = gen("");
    std::locale::global(loc);
    std::string str2 = boost::locale::to_upper("001Öä", loc); // application crashes here.
    std::string str3 = boost::locale::to_upper("001Öä"); // This also does not work
    }

The crash happens in the following function. this function throws bad cast exception.

template<class _Facet> inline
const _Facet& __CRTDECL use_facet(const locale& _Loc)

{   // get facet reference from locale
_BEGIN_LOCK(_LOCK_LOCALE)   // the thread lock, make get atomic
    const locale::facet *_Psave =
        _Facetptr<_Facet>::_Psave;  // static pointer to lazy facet

    size_t _Id = _Facet::id;
    const locale::facet *_Pf = _Loc._Getfacet(_Id);

    if (_Pf != 0)
        ;   // got facet from locale
    else if (_Psave != 0)
        _Pf = _Psave;   // lazy facet already allocated
    else if (_Facet::_Getcat(&_Psave, &_Loc) == (size_t)(-1))

#if _HAS_EXCEPTIONS

        _THROW_NCEE(bad_cast, _EMPTY_ARGUMENT); // lazy disallowed

....
....
....

}

Could you please help me out.

Regards, Sumit

1条回答
别忘想泡老子
2楼-- · 2019-02-17 15:34

I had this same issue when using a static library build of Boost 1.55 in a Visual Studio 2008 application on Windows 7 64-bit where both the main executable and a couple DLLs all linked to Boost. I'm not sure if your issue is the same as mine since you didn't mention using DLLs, but that wasn't something I thought was relevant when I first started investigating this.

If you're just interested in the most straight-forward way to fix this, then building Boost as a shared library should do it. Specifically, I mean setting the link property of the b2 command-line to shared rather than static.

Explanation

The reason using a static library build has a problem is due to Boost.Locale using std::locale::facet objects for doing its text conversion operations like upper-casing and normalizing. std::locale::facet classes are required to have an id static member variable whose unique value is assigned by the Standard Library implementation when it is constructed during static initialization.

The problem with this when using a static library is that all executables and DLLs get their own distinct copy of static member variables from a static library, as discussed in Shared global variable in C++ static library. When you use boost::locale::generator::operator() to generate a locale, it only installs the std::locale::facet objects into the locale that have an id member variable that is part of the same DLL or executable where that call is contained.

As I said above, the most straight-forward way to fix this is to build Boost as a shared library. That way there will only be one copy Boost.Locale's static member variables. Specifically, they'll be in the Boost.Locale DLL.

Alternative Solution

You can do this with a static library build of Boost by making sure all of std::locale::facet objects from all DLLs and the executable using Boost.Locale get installed into the std::locale object you're trying to use.

You can use some code like below to do this. For DLLs, you could call this in DllMain when its second parameter fdwReason is DLL_PROCESS_ATTACH, and for your executable, you could call it in WinMain or some other application entry point (if you're using something like MFC or Qt).

void setup_global_locale()
{
    const boost::locale::generator generator;
    const std::locale locale = generator.generate( std::locale(), "" );
    std::locale::global( locale );
}

The important piece of the code is that it's using the global locale as the base locale each time it's run, and it installs the newly-generated locale as the new global locale. This is different from what boost::locale::generator::operator() will do, since it uses std::locale::classic as the base locale, and that one cannot be modified. By calling this from each DLL and the executable, you'll install each of their std::locale::facet objects into the global locale.

查看更多
登录 后发表回答