【问题标题】:Sharing code between classes with runtime versus compile-time parameters在具有运行时参数与编译时参数的类之间共享代码
【发布时间】:2019-10-30 12:53:55
【问题描述】:

考虑一个模拟关联缓存的类:

template <size_t S, size_t L, size_t W>
class AssociativeCache {
  size_t which_set(size_t index) { return index % (L * W); }
  // ...
};

这里,关键缓存参数SLW是编译时非类型模板参数。

也可以像这样实现一个类似的类:

class AssociativeCacheDynamic {
  size_t S, L, L;
  size_t which_set(size_t index) { return index % (L * W); }
  // ...
};

这里的关键参数与其他情况相同,但存储为类的成员变量。

除了在动态情况下设置缓存参数值的构造函数外,两个类的每个方法的实现也基本相同,即源代码逐字节相同1支持>。

如果我想要 both 这两个类,我怎样才能以最少的代码重复和尽可能少的 grotty 宏或多个包含 hack 来实现它们?

如果解决方案允许您有一些差异,则加分:例如,模板方法可能使用std::array 存储,而动态方法可能使用std::vector


1虽然编译后的代码通常会完全不同(因为在动态情况下代码不是专门针对固定值的)。

【问题讨论】:

    标签: c++ templates code-reuse code-duplication


    【解决方案1】:

    一种方式可能如下:

    size_t which_set_impl(size_t index, size_t line_length, size_t ways_per_set)
    {
        return index % (line_length * ways_per_set);
    }
    
    template <size_t S, size_t L, size_t W>
    class AssociativeCache
    {
        public:
            size_t which_set(size_t index) { return which_set_impl(index, L, W); }
    };
    
    class AssociativeCache2
    {
        size_t size, line_length, ways_per_set;
        public:
            AssociativeCache2(size_t line_length_in, size_t ways_per_set_in) : line_length(line_length_in), ways_per_set(ways_per_set_in) {}
            size_t which_set(size_t index) { return which_set_impl(index, line_length, ways_per_set); }
    };
    
    int main(int argc, char** argv)
    {
        AssociativeCache<10, 10, 10> MyCache1 = {};
        AssociativeCache2 MyCache2(10, 10);
        std::cout << "1: " << MyCache1.which_set(10) << " and 2: " << MyCache2.which_set(10) << std::endl;
        return 0;
    }
    

    或者,您可以让两个关联缓存从公开成员的基类扩展,例如:

        class BaseCache
        {
            virtual size_t GetLineLength() = 0;
            virtual size_t GetWaysPerSet() = 0;
            size_t which_set(size_t index) { return index % (this->GetLineLength() * this->GetWaysPerSet());
        }
        class AssociativeCacheFoo : BaseCache
        {
            ...
            size_t GetLineLength() const final override { return line_length; }
        }
        template <size_t S, size_t L, size_t W>
        class AssociativeCacheBar : BaseCache
        {
           ...
            size_t GetLineLength() const final override { return L; }
        }
    

    上面的例子是伪代码,未经测试,不保证可以编译。

    并通过将这些访问器公开或将 which_set 设为它们两者的友元函数来将实现公开给辅助函数。

    【讨论】:

    • 这实际上并没有在AssociativeCacheAssociativeCache2 之间共享代码:每个方法都必须在每个类中以不同的方式编写,它只是将实现分解为另一个可以调用的方法两者,但我不想单独编写每个方法。此外,它严重依赖编译器来内联和不断传播模板案例中的值,否则您可能会得到错误的代码。
    【解决方案2】:

    诀窍是想出基类来封装两种形式之间的差异,同时允许派生的模板类拥有共同的功能。比如:

    #include <vector>
    
    template <size_t Sp, size_t Wp, size_t Lp>
    class AssociativeCacheTemplate {
    protected:
        static constexpr size_t S = Sp;
        static constexpr size_t W = Wp;
        static constexpr size_t L = Lp;
        AssociativeCacheTemplate() { }
    };
    
    class AssociativeCacheDynamic {
    protected:
        size_t S;
        size_t W;
        size_t L;
    public:
        AssociativeCacheDynamic(size_t Sp, size_t Wp, size_t Lp): S(Sp), W(Wp), L(Lp) { }
    };
    
    template <class T>
    class AssociativeCache: T {
        using T::L;
        using T::W;
        using T::S;
    public:
        using T::T;
        size_t which_set(size_t index) const { return index % (L * W); }
    };
    
    int test() {
        AssociativeCache<AssociativeCacheTemplate<2, 16, 32>> t;
        AssociativeCache<AssociativeCacheDynamic> d(2, 16, 32);
        return t.which_set(3) * d.which_set(2);
    }
    

    模板库中的static constexpr 允许将模板值用作代码中的常量,而不会占用任何内存。只要大小变量SWL 在两个基类之间相同,主AssociativeCache 类中的using 语句就可以访问这两个基类值。

    AssociativeCacheDynamic 构造函数的 public 是编译它所必需的。

    【讨论】:

      猜你喜欢
      • 2017-12-16
      • 2016-12-01
      • 1970-01-01
      • 2018-12-28
      • 2023-03-05
      • 2014-06-26
      • 2016-02-04
      • 1970-01-01
      • 2018-03-30
      相关资源
      最近更新 更多