статическая функция в качестве параметра шаблона класса — причина проблемы сопровождения

После того как я научился передавать статические функции (HashFunction) как класс (Collection<T,HashFunction>) параметр шаблона, я очень зависим от него.

Я использую его во многих местах … теперь я просто понимаю, что если я хочу изменить HashFunctionПодпись, я буду обязан изменить код в различных местах.

пример

Есть несколько классов (B а также C), которые предназначены для использования в качестве элемента пользовательской коллекции (Collection<T,HashFunction>): —

class B{
int bHash;
public: static int& getHash(B& b){return b.bHash;}     //#1
//.... other complex thing about B ....
};

class C{
int cHash1;
public: static int& getHash1(C& c){return c.cHash1;}   //#2
int cHash2;
public: static int& getHash2(C& c){return c.cHash2;}   //#3
//.... other complex thing about C ....
};
//There are about 20 places, i.e. #1 to #20
//They have a thing in common : return an integer field

Collection<T,HashFunction> (его код не показан) работает аналогично T,

Вот использование: —

Collection<B,&B::getHash> collectB;
Collection<C,&C::getHash1> collectC1;
Collection<C,&C::getHash2> collectC2;
//There are about 30+ locations.

проблема

Подпись хеш-функции (#1,#2,#3 и внутри Collection) может потребовать изменений в будущем.

Например, подпись может измениться с

int bHash;
static int& getHash(B& b){return b.bHash;}

в

HashStructure bHash; //"HashStructure" is a new class
static HashStructure& getHash(B& b,int notUsed){return b.bHash;}
//They tend to still have a thing in common : return a "HashStructure" field

//Here is the maximum possible difference :-
HashStructure bHash[3];
static HashStructure& getHash(B& b,int index){return b.bHash[index];}
//They are likely consistent for both B and C.

изменения Collection использовать новую подпись не сложно, но меняя все подписи #1 в #20 это утомительно.

Это указывает на проблему ремонтопригодности.

Вопрос

Предположим, я могу повернуть время вспять, когда есть только #1 в #3,
как изменить код / ​​дизайн (в примере), чтобы предотвратить проблему ремонтопригодности.

Мнения:

  • Я должен использовать наследование (A а также B выведено из нового класса),
    но это не подходит (Так как B может иметь неограниченное количество хэш-функций. Более того, имена хеш-функций, вероятно, отличаются от A«S.)

  • Некоторый определенный шаблон дизайна мог бы помочь. (?)

  • Шаблон Variadic и SFINAE могут помочь. (из комментария Даня, спасибо!)

2

Решение

Кажется, что getHash можно разложить на

template <typename T, int (T::*hash)>
int& getHash(T& t) { return t.*hash; }

Тогда использование это:

Collection<B, &getHash<B, &B::bHash>> collectB;
Collection<C, &getHash<C, &C::cHash1>> collectC1;
Collection<C, &getHash<C, &C::cHash2>> collectC2;

И позже вы можете изменить реализацию getHash один раз:

template <typename T, int (T::*hash)[3]>
static HashStructure& getHash(T& t, int index) { return (t.*hash)[index]; }
1

Другие решения

Чтобы не допустить проблемы с обслуживаемостью, я бы не использовал функции в качестве аргументов шаблона. Я бы пошел за 1 type == 1 hash function дизайн, аналогичный тому, что делает STL для решения той же проблемы.

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

Таким образом, при изменении подписи обновляются только сайты вызовов. Вы также можете предоставить обе подписи до тех пор, пока не будет обновлен каждый сайт вызовов, что позволит вам шаг за шагом обновлять кодовую базу.

Пример:

#include <utility>

class C {
static int cHash1;
};
int C::cHash1 = 0;

struct C1 : public C {
static int hash(C &value);
static int hash(C &value,bool);
};

struct C2 : public C {
static int hash(C &value);
static int hash(C &value,bool);
};

template <class Value, class HashFunction>
struct Collection {
using key_type = decltype(HashFunction::hash(std::declval<HashFunction&>()));
};

template <class Value, class HashFunction>
struct CollectionUpdated {
using key_type = decltype(HashFunction::hash(std::declval<HashFunction&>(), std::declval<bool>()));
};

int main() {
Collection<int, C1> c1;
Collection<int, C2> c2;
CollectionUpdated<int, C1> c1_up;
CollectionUpdated<int, C2> c2_up;
return 0;
}
1

По вопросам рекламы [email protected]