使用运行时参数与编译时参数在类之间共享代码

Sharing code between classes with runtime versus compile-time parameters

本文关键字:参数 之间 共享 代码 编译 运行时      更新时间:2023-10-16

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

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

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

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


1尽管编译后的代码通常会完全不同(因为代码不是专门用于动态情况下的固定值)。

诀窍是提出基类来封装两种形式之间的差异,同时允许派生模板类保存通用功能。 像这样:

#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是使其编译所必需的。

一种方法可能如下所示:

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对它们都作为友元函数来向帮助程序函数公开实现。