4

Some code I'm working with uses std::call_once so that some initialization only occurs once. However, there are global objects with constructors that can end up calling the initialization code.

In the following sample, call_once actually gets called twice. I guess it's because the once_flag constructor hasn't ran before it gets used. Is there a way around this so that some initialization code only gets called once without having to prohibit globals?

#include <mutex>
#include <iostream>

using namespace std;

void Init();

class Global
{
public:
    Global()
    {
        Init();
    }
};

Global global;

once_flag flag;

void Init()
{
    call_once(flag, []{  cout << "hello" << endl;  });
}



int main(int argc, char* argv[])
{
    Init();
    return 0;
}

Output is:

hello
hello
Scott Langham
  • 58,735
  • 39
  • 131
  • 204
  • 2
    I only see "hello" output once for GCC and Clang. –  Nov 27 '14 at 09:38
  • @remyabel - Hmm, I wonder then if this is a micosoft STL bug. – Scott Langham Nov 27 '14 at 09:39
  • Your `using namespace std;` defeats the namespace mechanism of C++. It also makes your code harder to understand. Don't do that. Rather, `using std::once_flag;` etc. That is, import specific symbols; don't dump the namespace. Also, for standard C++ style, avoid capitalizing function names. – thb Sep 17 '17 at 11:12

1 Answers1

4

According to the specs, once_flag should have a trivial constexpr constructor (for example see here - http://en.cppreference.com/w/cpp/thread/once_flag ). With this in place, if it is global/static, it is not actually "constructed" (no actual function is executed), but more like "value initialized" - like any global/static POD type. In that case it is not possible to have any constructor run "before" this once_flag is properly initialized. Given your comment about using MSVC, I guess it can be a bug in the implementation...

EDIT: According to the comment below, constexpr is not supported on MSVC at all, so your options are really limited in here... If you have everything in one file, just put your once_flag "above" everything that uses it - the constructors in a file are executed in the order of declaration of objects. If you have the users spread across different files, your only option is to use a function that provides access to static internal once_flag - like in this answer http://www.parashift.com/c++-faq/static-init-order-on-first-use.html .

Freddie Chopin
  • 8,440
  • 2
  • 28
  • 58
  • 5
    VS2013 does not support `constexpr` at all – Piotr Skotnicki Nov 27 '14 at 09:46
  • I wonder if a static function will have the problem though (as my version of MSVC doesn't support magic statics) that the once_flag could be constructed twice in the face of multiple threads. Hmm.. sounds like a job for std::call_once to ensure that doesn't happen, hmm, hang on a minute. :) – Scott Langham Nov 27 '14 at 09:55
  • `once_flag` constructor is not trivial at all: it sets the internal state to indicate that no function has been called yet. – Anton Savin Nov 27 '14 at 09:59
  • 2
    @AntonSavin - it must be `constexpr`, which means it must be _REALLY_ simple, so simple that this type can be treated almost like a POD from this point of view. – Freddie Chopin Nov 27 '14 at 10:16
  • Indeed, the signature `constexpr once_flag() noexcept;` leaves no room for interpretation on a conformant toolchain. – ildjarn Nov 27 '14 at 10:17
  • @AntonSavin The `constexpr` constructor guarantees *constant initialization*, which as a form of static initialization must be performed before any dynamic initialization takes place. – T.C. Nov 28 '14 at 08:23