Nifty counter in precompiled header does not work

134 Views Asked by At

To fix a problem caused by the static (de-)initialization order fiasco I tried to use the Nifty Counter idiom aka Schwartz Counter. I noticed, however, that it does not work if the header file containing the static initializer is included inside Visual Studio's precompiled header. In such a case I see in the debugger, that the constructor of the static initializer is called after the constructor of the other static object.

If I declare an additional static initializer right after #include "stdafx.h" or edit the precompiled header to not contain the declaration it works as expected.

Any ideas what might cause this?

EDIT


I was finally able to reproduce this problem in a new dummy project:

Foo.cpp

#include "stdafx.h"
#include "Foo.h"

#include <cassert>
#include <iostream>

#ifdef NIFTY
static int SchwartzCounter; // zero initialized at load time
static typename std::aligned_storage<sizeof(Foo), alignof (Foo)>::type foo_buf;

Foo& foo = reinterpret_cast<Foo&>(foo_buf);
#else
Foo foo;
#endif

Foo::Foo()
{
    std::cout << __func__ << std::endl;
}

Foo::~Foo()
{
    std::cout << __func__ << std::endl;
}

void Foo::doSomething()
{
    std::cout << __func__ << std::endl;
    assert(x == 42);
}

#ifdef NIFTY
FooInitializer::FooInitializer()
{
    std::cout << __func__ << std::endl;

    if (SchwartzCounter++ == 0)
    {
        new (&foo) Foo();
    }
}

FooInitializer::~FooInitializer()
{
    std::cout << __func__ << std::endl;

    if (--SchwartzCounter == 0)
    {
        (&foo)->~Foo();
    }
}
#endif

Foo.h

#pragma once
class Foo
{
public:
    Foo();
    ~Foo();

    void doSomething();

private:
    int x = 42;
};

#ifdef NIFTY
extern Foo& foo;

static struct FooInitializer {
    FooInitializer();
    ~FooInitializer();
} fooInitializer;
#else
extern Foo foo;
#endif

Bar.cpp

#include "stdafx.h"
#include "Foo.h"
#include "Bar.h"

#include <cassert>
#include <iostream>

#ifdef NIFTY
static int SchwartzCounter; // zero initialized at load time
static typename std::aligned_storage<sizeof(Bar), alignof (Bar)>::type bar_buf;

Bar& bar = reinterpret_cast<Bar&>(bar_buf);
#else
Bar bar;
#endif

Bar::Bar()
{
    std::cout << __func__ << std::endl;
    foo.doSomething();
}

Bar::~Bar()
{
    std::cout << __func__ << std::endl;
}

void Bar::doSomething()
{
    std::cout << __func__ << std::endl;
    assert(x == 42);
}

#ifdef NIFTY
BarInitializer::BarInitializer()
{
    std::cout << __func__ << std::endl;

    if (SchwartzCounter++ == 0)
    {
        new (&bar) Bar();
    }
}

BarInitializer::~BarInitializer()
{
    std::cout << __func__ << std::endl;

    if (--SchwartzCounter == 0)
    {
        (&bar)->~Bar();
    }
}
#endif

Bar.h

#pragma once
class Bar
{
public:
    Bar();
    ~Bar();

    void doSomething();

private:
    int x = 42;
};

#ifdef NIFTY
extern Bar& bar;

static struct BarInitializer {
    BarInitializer();
    ~BarInitializer();
} barInitializer;
#else
extern Bar bar;
#endif

stdafx.h

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

// If the following define is commented out, the nifty counter idiom is not used
#define NIFTY

// If the following include is commented out while the nifty counter idiom is used, the initialization order is correct
#include "Foo.h"
0

There are 0 best solutions below