1

I'm having problems writing a wrapper class using the boost::locale::date_time library. Specifically, I cannot create a global object from my class, though everything works fine other than that.

Here's relevant sample code:

// DateTimeWrapper.h

#include <boost\\locale\\date_time.hpp>
#include <boost\\locale.hpp>

class DateTimeWrapper
{
public:
    DateTimeWrapper();
    ~DateTimeWrapper();

    // ... Other methods...

protected:

    boost::locale::date_time* m_date_time;
    static void Init_Global_Locale();
    static bool m_Global_Locale_Initialized;
};
// DateTimeWrapper.cpp

bool DateTimeWrapper::m_Global_Locale_Initialized = false;

DateTimeWrapper::DateTimeWrapper()
{
    Init_Global_Locale();

    // The following line will work for the local object,
    // but throws a std::bad_cast exception for the global object
    m_date_time = new boost::locale::date_time;
}

DateTimeWrapper::~DateTimeWrapper()
{
    delete m_date_time;
}

void DateTimeWrapper::Init_Global_Locale()
{
    if (!m_Global_Locale_Initialized)
    {
        boost::locale::generator gen;
        std::locale l = gen("");
        std::locale::global(l);

        m_Global_Locale_Initialized = true;
    }
}

// This object throws a std::bad_cast exception.  Code runs normally if I comment out the following line.
DateTimeWrapper global_date_time_object;

int main()
{
    // This object works just fine
    DateTimeWrapper local_date_time_object;

    // Do stuff with local_date_time_object...

    return(0);
}

As you can see in the code, I use a static member to make sure the global locale is initialized the first time a DateTimeWrapper object is created. Normally, this prevents a std::bad_cast exception from being thrown when I create my boost::locale::date_time member. However, I still receive the exception from that line when the first DateTimeWrapper object created is a global instance.

Note that stepping through the debugger, I can confirm that all lines in the Init_Global_Locale() method are run during construction of the global object. This sample code also declares DateTimeWrapper::m_Global_Locale_Initialized before it declares global_date_time_object, within the same source file, so I know that order-of-initialization is not the problem here (confirmed by stepping through with the debugger anyhow).

So why does the code work for local objects but not global objects, even though I can see all lines of code are run through, in the correct order, for both versions?

user3236291
  • 133
  • 7
  • 2
    `#include ` should be `#include `, and likewise for the other one – Eljay Apr 22 '20 at 11:15
  • offtopic: in include use always unix style directory delimiters `/`. VS handles this properly without warning. – Marek R Apr 22 '20 at 11:16
  • Here is [**minimal** complete verifiable example](https://wandbox.org/permlink/kj2p66bQtWHT6Msk). – Marek R Apr 22 '20 at 11:28
  • The above code worked on my machine (after fixing slashes in includes). I did have to add `-lboost_system-mt -lboost_locale-mt` on my compile line (and I compiled as `-std=c++17`, using Boost 1.72). – Eljay Apr 22 '20 at 11:39
  • @MarekR I have a helper class containing only static methods. One of those methods uses a static, set-in-stone historical date - which I declared as a static member of the helper class. What would be a better way to define and use this date if not as a static (i.e., global) member variable? The only alternative I see is to create a local version of the date inside the static method each time I call it, which is not only inefficient but also less transparent then I would like (that date would be buried inside the function rather than at the top of the file, where it is easy to see or tweak). – user3236291 Apr 22 '20 at 11:45
  • @user3236291 Am I not doing something that you were doing?? Because [the code](https://wandbox.org/permlink/C9dRs2InDkyOqb18) is working fine for me! (Also it didn't required me any extra flags...) – brc-dd Apr 22 '20 at 11:58

1 Answers1

0

I cannot reproduce the error (with the code shown in a single TU).

My hunch is that you have void DateTimeWrapper::Init_Global_Locale() and m_Global_Locale_Initialized defined in another translation unit, which lands you in SIOF (static initialization order fiasco).

Besides, there are numerous complexities about your types inviting errors (using non-owned pointers, not following Rule-Of-Zero/Three/Five). I'd write it much simpler, using C++11's function local static initialization:

#include <boost/locale.hpp>
#include <boost/locale/date_time.hpp>

struct EnsureLocaleBase {
    EnsureLocaleBase() { Init(); }

  private:
    static bool Init() {
        static auto const s_init = [] {
            boost::locale::generator gen;
            std::locale l = gen("");
            std::locale::global(l);
            return true;
        }();
        return s_init;
    }
};

class DateTimeWrapper : EnsureLocaleBase {
    boost::locale::date_time m_date_time;
};

DateTimeWrapper global_date_time_object;

int main() {
    DateTimeWrapper local_date_time_object;
}

Function local statics don't suffer SIOF and also are thread-safely initialized.

sehe
  • 374,641
  • 47
  • 450
  • 633