代码之家  ›  专栏  ›  技术社区  ›  stimulate

生成代码(在编译时)以调用模板的每个实例化的静态函数

  •  0
  • stimulate  · 技术社区  · 6 年前

    我的问题可以用下面的例子来描述。

    我想从模板的每个实例化中调用一个静态函数。我想的是一个元组,每次遇到模板的新实例化时,它都会被扩展,这样我就可以递归地访问该元组,并调用元组中每个类型的静态函数。

    #include <vector>
    
    template<typename T>
        struct Junk
        {
            static std::vector<T> all;
            Junk(T t)
            {
                all.push_back(t);
            }
    
            static void clear()
            {
                all.clear();
            }
        };
    
    template<typename T>
        std::vector<T> Junk<T>::all;
    
    void clearJunk()
    {
        Junk<float>::clear();
        Junk<char>::clear();
        Junk<unsigned int>::clear();        
        // how can I generalize this?
    }
    
    int main()
    {
        // instantiate Junk with different types
        Junk<float> f1(1.0f);
        Junk<float> f2(2.0f);
        // Junk<float>::all.size() == 2
    
        Junk<char> c1('a');
        Junk<char> c2('b');
        Junk<char> c3('c');
        // Junk<char>::all.size() == 3
    
        Junk<unsigned int> i1(1);
        // Junk<unsigned int>::all.size() == 1
    
        // clear all Junk
        clearJunk();
    
        return 0;
    }
    

    对此的运行时解决方案是函数指针的向量,每个模板实例化的第一个对象将指向该向量的静态成员函数的指针推送到该向量:

    std::vector<void(*)()> clear_funcs;
    
    template<typename T>
        struct Junk
        {
            static std::vector<T> all;
            Junk(T t)
            {
                (void)init;   // mention init at least once so its constructor will be invoked
                all.push_back(t);
            }
    
            static void clear()
            {
                all.clear();
            }
            struct At_Init
            {
                At_Init()
                {
                    clear_funcs.push_back(&Junk<T>::clear);
                }
            };
            static At_Init init; // will only be constructed once for each template instantiation
        };
    
    template<typename T>
        std::vector<T> Junk<T>::all;
    
    template<typename T>
        typename Junk<T>::At_Init Junk<T>::init;
    
    void clearJunk()
    {
        // call every clear() of all the Junk template instantiations
        for (void(*clear)() : clear_funcs) {
            clear();
        }
    }
    

    但是这个解决方案没有编译时的解决方案快,而且它需要类中很多根本没有意义的代码,所以我不喜欢这个解决方案,尽管它有效。

    如何在编译时生成代码来调用模板的每个实例化的静态函数?

    1 回复  |  直到 6 年前
        1
  •  3
  •   Yakk - Adam Nevraumont    6 年前

    你运气不好。

    为了了解原因,假设有3个动态库。他们每个人都使用 Junk<typeX> 在哪里? X 是0、1或2。

    这些库在程序运行后加载,具体取决于月球的相位。

    没有一个中心位置可能知道 Junk<?>::clear() 在编译时调用的方法。为了知道要调用哪些方法,您必须有一个负责它的中心位置,并在运行时跟踪 Junk<?> 类型被实例化。

    现在,您可能没有使用动态库,但事实上,该语言(实际上)支持这一点,这意味着无法跟踪交叉编译单元,即从模板实例化的所有类型的枚举,而不将其存储为运行时状态。在编译时,每个编译单元(cpp文件)可以单独编译。

    当然,有办法解决这个问题;如果您只有一个编译单元(甚至一个unity构建),或者如果您维护了一个受支持类型的中央列表(如果错过类型,还可以选择生成硬编译时错误),那么您可以生成与静态代码大小写类似的代码。

    但在此之前,请分析您的简单动态解决方案,并确保它是一个实际问题。