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

时间:2018-07-12 17:24:29

标签: c++ templates generic-programming

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

我想从模板的每个实例中调用静态函数。我在想的是像元组那样的结构,每次遇到模板的新实例时,该元组都会扩展,以便我可以递归访问该元组并调用该元组中每种类型的静态函数。

#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 个答案:

答案 0 :(得分:3)

你不走运。

要了解原因,请想象有3个动态库。他们每个人都使用Junk<typeX>,其中X是0、1或2。

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

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

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

自然有很多方法可以解决这个问题;如果您只有一个编译单元(甚至是一个统一的构建),或者维护了受支持的类型的中央列表(如果错过了类型,还可以选择生成硬编译时错误),则可以生成类似静态代码案例的代码。 / p>

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