Совместное использование кода между классами с параметрами времени выполнения и параметров времени компиляции - PullRequest
1 голос
/ 17 июня 2019

Рассмотрим класс, который имитирует ассоциативный кеш:

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

Здесь параметры кеша ключей S, L и W являются параметрами нетипового шаблона времени компиляции.

Можно также реализовать подобный класс, подобный этому:

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

Здесь ключевые параметры такие же, как и в другом случае, но вместо этого сохраняются как переменные-члены класса.

За исключением конструктора, который устанавливает значения параметров кэша в динамическом случае, реализация каждого метода двух классов также по существу идентична, т. Е. Исходный код является побайтово-одинаковым 1 .

Если я хочу обоих этих классов, как я могу реализовать их с наименьшим дублированием кода и с как можно меньшим количеством неуклюжих макросов или несколькими включенными хаки?

Бонусуказывает, если решение позволяет иметь некоторые различия: например, метод шаблона может использовать std::array хранилище, а динамический метод может использовать std::vector.


1 Хотя скомпилированный код часто будет радикально отличаться (поскольку код не специализирован для фиксированных значений в динамическом случае).

Ответы [ 2 ]

2 голосов
/ 17 июня 2019

Хитрость заключается в том, чтобы придумать базовые классы для инкапсуляции различий между двумя формами, в то же время позволяя производному шаблонному классу содержать общие функциональные возможности.Примерно так:

#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 в базе шаблонов позволяет использовать значения шаблона в качестве констант в коде, не занимая при этом никакой памяти.И пока переменные размера S, W и L одинаковы для двух баз, операторы using в основном классе AssociativeCache будут иметь доступ к значениям обоих базовых классов.

public для конструктора AssociativeCacheDynamic был необходим для его компиляции.

0 голосов
/ 17 июня 2019

Один из способов может быть следующим:

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 в качестве функции-друга для них обоих.

...