3

Today I learned that when we have a C++ class template with a static member variable, its constructor won't be called (in fact the member won't even be defined) unless we "use it in a way that requires the definition of the static data member to exist".

This phenomenon is very nicely explained here: C++ Static member initalization (template fun inside)

In practice, it means we have to explicitly refer to each instantiation of that static member (from outside the class template) if we want initialization (and any possible side effects of it) to take place.

I've been thinking about ways to overcome this issue.

My motivation is that there's an existing code base which uses various instantiations of class template Foo (it has multiple template parameters but I simplified that for the sake of the example) and I would like to automatically collect information about all the different parameter combinations.

I cannot practically wait for all these Foos to be constructed during program execution (it's a long running background process) so I thought that I could put a static Initializer<T> inside Foo<T> and have it extract the desired type information for each distinct Foo type right after the program starts.

In this case, having to enumerate all the instantiations of Initializer<T> Foot<T>::init in order to have the initializers run in the first place obviously defeats the purpose. I would have to go and see (all over the project) what the types are and that is precisely what I'm trying to automate.

I noticed that if I replace static member variable with static method holding a local static Initializer instance, I can force generation of Initializer<T> definitions more easily. I just have to take a pointer to that method (e.g. inside Foo's constructor).

The last step is to call this static method after the program starts. In case of g++/clang, using __attribute__((constructor)) works like a charm.

I also have to deal with MSVC++ though and this is what I came up with:

#include <iostream>

#if defined(_MSC_VER) && !defined(__clang__)
#define MSVC_CONSTRUCTOR_HACK
#define ATTRIBUTE_CONSTRUCTOR
#else
#define ATTRIBUTE_CONSTRUCTOR __attribute__((constructor))
#endif

static int& gInt() { // Global counter
    static int i;
    return i;
}

template <class T>
struct Initializer {
    // If it works, this gets called for each Foo<T> declaration
    Initializer() {
        gInt()++;
    }
};


#ifdef MSVC_CONSTRUCTOR_HACK
__pragma(section(".CRT$XCU", read))
template <class T>  // This will hold pointers to Foo<T>::getInit
static void(*g_constructors__)(void);
#endif

template <class T>
struct Foo {
    ATTRIBUTE_CONSTRUCTOR // Empty in case of MSVC
    static void getInit() {
        static Initializer<T> init;
    }

#ifdef MSVC_CONSTRUCTOR_HACK
    template <> // Why is this allowed?!
    __declspec(allocate(".CRT$XCU")) static void(*g_constructors__<T>)(void) = getInit;
#endif

    Foo() { // This never gets called and we want that
        std::cout << "Constructed Foo!" << std::endl; 
        (void)&getInit; // This triggers instantiation and definition of Initializer<T>
    }
};


void unused() {
    Foo<char> c;
    Foo<double> d;
    Foo<int> i;
    Foo<float> f;
}

int main() {
    std::cout << gInt() << std::endl; // prints 4
    return 0;
}

It relies on putting function pointers into the .CRT section of the executable (https://stackoverflow.com/a/2390626/6846474, https://github.com/djdeath/glib/blob/master/glib/gconstructor.h).

In order to make it work in this context, though, I also had to resort to this really strange hack: There's a global variable template g_constructors__ which is explicitly specialized inside (!) Foo.

To be honest, I was really surprised this works. I know it is non-standard but can someone explain how is it that it even compiles? Is it just luck or is it somehow "well-formed" at least as far as Microsoft C++ goes?

I am aware I could do this using some kind of external static analysis tool instead, but this is pretty close to what I want with the major advantage that it's all incorporated into the program being inspected.

If I can invoke Initializer for each T (and it looks like I can), extracting the type info is easy. I can stringify the template parameters using boost typeindex or anything else I need. The global counter was used here only to see if Initializer instances are being created or not.

  • Oops, formating issue. Just a missing curly brace. I fixed it, so it should work now. It compiles at least in Visual Studio 2017. –  Aug 30 '17 at 21:57
  • It looks like your real question is how to collect information on which template variants are being instantiated in your program. I don't know the exact solution, but it probably has to do with parsing object code and / or using some dedicated tools to introspect AST (maybe something clang-based?). – user7860670 Aug 30 '17 at 21:59
  • @VTT Well, I thought about various approaches including an external program (or a clang plugin) that would perform additional static analysis on the existing code, but I'd rather incorporate the type extraction into the program itself. Here I'm really curious about this piece of code. Any suggestions on how to do it differently are also welcome though. –  Aug 30 '17 at 22:04

3 Answers3

3

If you're willing to add the cost of an extra variable to your objects, this seems to do what you want, though this is all very complicated and I could be missing a case:

#include <iostream>

static int& gInt() { // Global counter
    static int i;
    return i;
}

struct Initializer {
    Initializer() { ++gInt(); }
};


template <class T>
struct Foo {
    Foo() { // This never gets called and we want that
        std::cout << "Constructed Foo!" << std::endl; 
    }
  private:
    static Initializer gint_incrementer;
    void* p_ = &gint_incrementer; // force existence 
};

template <typename T>
Initializer Foo<T>::gint_incrementer;

void unused() {
    Foo<char> c;
    Foo<char> c2;
    Foo<double> d;
    Foo<int> i;
    Foo<float> f;
}

int main() {
    std::cout << gInt() << std::endl; // prints 4
}
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • Well, it's much simpler than my take. Interesting... I thought I tried something like that before and got the impression it does not work. Obviously I must have missed something because your code is pretty much equivalent and without hacks. Thank you. –  Aug 30 '17 at 22:26
  • Yes, I remember now. I had a dummy function foo in Initializer and called it inside Foo's constructor. In this simple example it does the same job as the pointer, although I am sure it didn't work in the real project I was working on. It didn't generate Initializer definition and I was getting linker errors. Could it be somehow caused by multiple translation units or something? I'm running out of ideas... –  Aug 30 '17 at 22:38
  • Nevermind, I solved it. Your approach indeed works. I had a partial specialization of Foo on top of all that and I forgot to define Foo::gint_incrementer for it. It has to be redefined like the whole class body itself. –  Aug 31 '17 at 07:36
  • This makes counter work but how does this help to figure out which template variants were instantiated? – user7860670 Aug 31 '17 at 10:05
  • @VTT it doesn't, and although that's in the title there isn't anything in the code or question to make me think that's what OP actually wants, but if so then this isn't it – Ryan Haining Aug 31 '17 at 12:02
  • It does help considerably. In my original code, Initializer is also templated and that's really all you need. Any code inside Initializer class will be generated and its constructor ran (!) for every T which is used when declaring various Foo's. The point was to create Initializer instance for each declaration of Foo. I already edited the original question and I mention boost typeindex which can be used to turn template parameter names into strings. We can then put those strings in a shared data structure (synchronized across threads of course) and we're done. –  Aug 31 '17 at 12:20
  • Sorry, It looks like I created a lot of confusion. When I have the time, I'll probably answer my own question with more code. I thought the idea was simple enough but I could have probably formulated it better. Thanks for your help anyway! –  Aug 31 '17 at 12:33
1

It can be achieved with no hacks and no need for an unused() function if we use a variation of the schwartz counter idiom:

global_counter.hpp

#pragma once

struct GlobalCounter {
    int next();
    int value() const;
    int value_ = 0;
    struct init {
        init();
        ~init();
    };
};

extern GlobalCounter& globalCounter;
static GlobalCounter::init globalCounterInit;

global_counter.cpp

#include "global_counter.hpp"

#include <memory>
#include <type_traits>

static int globalCounterCount;
static std::aligned_storage_t <sizeof(GlobalCounter), alignof(GlobalCounter)> globalCounterStorage;
GlobalCounter& globalCounter = reinterpret_cast<GlobalCounter&>(globalCounterStorage);

GlobalCounter::init::init() {
    if(globalCounterCount++ == 0) new (&globalCounter) GlobalCounter ();
}

GlobalCounter::init::~init() {
    if (--globalCounterCount == 0) globalCounter.~GlobalCounter();
}

int GlobalCounter::next() {
    return value_++;
}

int GlobalCounter::value() const {
    return value_;
}

foo.hpp

#pragma once

#include "global_counter.hpp"
#include <iostream>

template <class T>
struct Foo {
    Foo() { // This never gets called and we want that
        std::cout << "Constructed Foo!" << std::endl;
    }

    static int ident;
};

template<class T>
int Foo<T>::ident;

template<class T>
struct EnableFoo
{
    EnableFoo()
    {
        if (counter++ == 0)
            Foo<T>::ident = globalCounter.next();
    }

    static int counter;
};
template<class T> int EnableFoo<T>::counter;



static EnableFoo<char> enableFooChar;
static EnableFoo<int> enableFooInt;
static EnableFoo<double> enableFooDouble;
static EnableFoo<float> enableFooFloat;

main.cpp

#include <iostream>
#include "foo.hpp"

int main() {

    std::cout << globalCounter.value() << std::endl; // prints 4
    return 0;
}

See schwartz counter:

https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
0

OK, because the solution in my question was only partial, I'll share a working code snippet that actually shows what I was after, now that I figured it out.

We don't need any compiler-specific hacks, the following should work universally:

#include <iostream>
#include <vector>
#include <string>
#include <mutex>
#include <boost/type_index.hpp>

static std::vector<std::string>& getInstantiations() {
    static std::vector<std::string> instantiations;
    return instantiations;
}

static std::mutex& getMutex() {
    static std::mutex mut;
    return mut;
}

template <class T>
struct Introspection {
    Introspection() { 
        std::lock_guard<std::mutex> lock(getMutex());
        getInstantiations().push_back(boost::typeindex::type_id<T>().pretty_name());
    }
    void forceExistence() {}
};


template <class... Ts>
struct Foo {
    Foo() { // This never gets called and we want that
        std::cout << "Constructed Foo!" << std::endl;
        introspection.forceExistence();
    }
private:
    static Introspection<Foo<Ts...>> introspection;
};

template <class... Ts>
Introspection<Foo<Ts...>> Foo<Ts...>::introspection;

void unused() {
    Foo<char> c;
    Foo<char> c2;
    Foo<double> d;
    Foo<int, const int> i;
    Foo<float, bool, long> f;
}

int main() {
    for (auto& i : getInstantiations()) {
        std::cout << i << std::endl;
    }

    /*
        output:

        Foo<char>
        Foo<double>
        Foo<int, int const>
        Foo<float, bool, long>
    */
}

It looks silly but consider a larger project with Foo<...> declarations all over the place. Yes, maybe I could just use a regex search but this way I can actually work with the gathered information while the inspected program is running. Echoing the type names is just the simplest example of what we could do with this.