c++ - 收集有关在程序中实例化哪些模板变体的信息

标签 c++ templates initialization static-members

今天我了解到,当我们有一个带有静态成员变量的 C++ 类模板时,它的构造函数将不会被调用(事实上,该成员甚至不会被定义),除非我们“以需要要存在的静态数据成员的定义”。

这里很好地解释了这种现象: C++ Static member initalization (template fun inside)

在实践中,这意味着如果我们想要进行初始化(以及它的任何可能的副作用),我们必须显式引用该静态成员的每个实例(从类模板外部)。

我一直在思考解决这个问题的方法。

我的动机是有一个现有的代码库,它使用类模板的各种实例化 Foo (它有多个模板参数,但我为了示例简化了它)我想自动收集有关所有不同参数组合的信息。

我几乎等不及所有这些Foo s 将在程序执行期间构建(这是一个长时间运行的后台进程)所以我认为我可以放置一个静态的 Initializer<T>里面Foo<T>并让它为每个不同的 Foo 提取所需的类型信息在程序启动后立即键入。

在这种情况下,必须枚举 Initializer<T> Foot<T>::init 的所有实例化为了让初始化器首先运行显然违背了目的。我将不得不去看看(在整个项目中)类型是什么,而这正是我想要自动化的。

我注意到如果我用持有局部静态变量的静态方法替换静态成员变量 Initializer例如,我可以强制生成 Initializer<T>定义更容易。我只需要获取指向该方法的指针(例如在 Foo 的构造函数中)。

最后一步是在程序启动后调用这个静态方法。对于 g++/clang,使用 __attribute__((constructor))就像一个魅力。

不过,我还必须处理 MSVC++,这就是我想出的:

#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;
}

它依赖于将函数指针放入可执行文件的 .CRT 部分(https://stackoverflow.com/a/2390626/6846474https://github.com/djdeath/glib/blob/master/glib/gconstructor.h)。

不过,为了让它在这种情况下工作,我还不得不求助于这个非常奇怪的 hack:有一个全局变量模板 g_constructors__在 (!) Foo 中明确专门化.

老实说,我真的很惊讶这个效果。我知道它是非标准的,但有人可以解释它是如何编译的吗?这仅仅是运气,还是至少就 Microsoft C++ 而言它以某种方式“格式良好”?

我知道我可以改用某种外部静态分析工具来做到这一点,但这与我想要的非常接近,主要优点是它都被纳入了被检查的程序。

如果我可以为每个 T 调用 Initializer(看起来我可以),提取类型信息就很容易了。我可以使用 boost typeindex 或我需要的任何其他东西对模板参数进行字符串化。此处使用全局计数器只是为了查看是否 Initializer是否正在创建实例。

最佳答案

如果您愿意为您的对象添加一个额外变量的成本,这似乎可以满足您的要求,尽管这一切都非常复杂,而且我可能会遗漏一个案例:

#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
}

关于c++ - 收集有关在程序中实例化哪些模板变体的信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45970190/

相关文章:

c++ - 类赋值运算符和复制构造函数

c# - 如何计算 Box2D/Farseer 中夹具和点之间的距离?

c++ - 使用 Directx 10 时,我得到了很多宏重新定义

c++ - 如何从 Windows 7 的崩溃错误中获取更多信息?

c++ - 如何将预建库添加到 VC++ 解决方案中?

angularjs - AngularJS 中元素的指令模板唯一 ID

c++ - sizeof 和函数模板 : sizeof(&f) vs sizeof(&f<int>)

c++ - 模板参数推导失败

python - 如何在 Python 中用零初始化一个整数 array.array 对象

c - 在此函数中使用未初始化的数组