编程语言
首页 > 编程语言> > c – 收集有关在程序中实例化哪些模板变体的信息

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

作者:互联网

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

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

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

我一直在想办法克服这个问题.

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

我实际上无法等待在程序执行期间构建所有这些Foos(这是一个长时间运行的后台进程)所以我认为我可以放置一个静态初始化器< T>在Foo< T>内并在程序启动后立即为每个不同的Foo类型提取所需的类型信息.

在这种情况下,必须枚举Initializer< T>的所有实例化. Foot< T> :: init以使初始化器在第一位运行明显地失败了目的.我必须去看看(整个项目)类型是什么,这正是我想要自动化的东西.

我注意到如果我用静态方法替换静态成员变量来保存本地静态初始化器实例,我可以强制生成初始化器< T>定义更容易.我只需要指向该方法(例如在Foo的构造函数中).

最后一步是在程序启动后调用此静态方法.在g / clang的情况下,使用__attribute __((构造函数))就像一个魅力.

我也必须处理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/6846474,https://github.com/djdeath/glib/blob/master/glib/gconstructor.h).

但是,为了使它在这种情况下工作,我还不得不求助于这个非常奇怪的黑客攻击:有一个全局变量模板g_constructors__,它明确地专门用于(!)Foo.

说实话,我真的很惊讶这个作品.我知道这是非标准的,但有人可以解释它甚至是如何编译的吗?难道它只是运气还是至少就微软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,templates,initialization,static-members
来源: https://codeday.me/bug/20190828/1750614.html