CTTCG 28 Debugging Templates

Shallow Instantiation

作用:早点暴露参数校验错误,避免在一堆模板实例化失败的信息中找错误。

可以用 static_assert 或者定义无用类:

这样在实例化 shell 函数的时候还会实例化其中的 ShallowChecks 类,从而确保参数能够解引用。

Archetypes

作用:测试文档的描述是否精确定义了模板算法对参数的要求。

原型是模拟文档中描述的行为的最小类型。比如文档可能如下:

// T must be EqualityComparable, meaning:
// two objects of type T can be compared with == and the result converted to bool
template<typename T>
int find(T const* array, int n, T const& value);

而实现可能如下:

template<typename T>
int find(T const* array, int n, T const& value) {
    int i = 0;
    while(i != n && array[i] != value)
        ++i;
    return i;
}

实际上使用的是 != 操作符,这需要被检查出来。如果改成:

template<typename T>
int find(T const* array, int n, T const& value) {
    int i = 0;
    while(!(i == n) && !(array[i] == value))
        ++i;
    return i;
}

又需要保证比较的结果能够使用 ! 单目运算符,同时结果还能上下文转换成 bool 类型。如果比较结果可以转换成 bool 类型,但是其 ! 运算符被显式删除,则不能满足上述条件。写 Archetype 其实就是在写测试。

Tracers / Oracles

作用:收集信息,检查模板算法的执行流程,评估模板算法的性能等。

都指的是能满足约束的参数,而且这种参数具有收集信息的功能:

class SortTracer {
private:
    int value; // integer value to be sorted
    int generation; // generation of this tracer
    inline static long n_created = 0; // number of constructor calls
    inline static long n_destroyed = 0; // number of destructor calls
    inline static long n_assigned = 0; // number of assignments
    inline static long n_compared = 0; // number of comparisons
    inline static long n_max_live = 0; // maximum of existing objects

    ...

public:
    // constructor
    SortTracer (int v = 0) : value(v), generation(1) {
        ++n_created;
        update_max_live();
        std::cerr << "SortTracer #" << n_created
            << ", created generation " << generation
            << " (total: " << n_created - n_destroyed
            << ")\n";
    }

    // copy constructor
    SortTracer (SortTracer const& b)
        : value(b.value), generation(b.generation+1) {
        ++n_created;
        update_max_live();
        std::cerr << "SortTracer #" << n_created
            << ", copied as generation " << generation
            << " (total: " << n_created - n_destroyed
            << ")\n";
    }

    // destructor
    ~SortTracer() {
        ++n_destroyed;
        update_max_live();
        std::cerr << "SortTracer generation " << generation
            << " destroyed (total: "
            << n_created - n_destroyed << ")\n";
    }

    ...

如果某个算法模板使用了上面的构造、析构等功能,这个类型的静态属性就能收集到数据。在算法运行前后统计数据的差值就能知道算法对各操作符或特殊函数的调用次数。