Создание идентификационного номера на основе типа в старом коде с минимальными изменениями

Я поддерживаю очень старую (20+ лет) и довольно большую (15 KLOC) библиотеку, в которой есть различные типы объектов, которые в настоящее время идентифицируются целым числом. Это ставит проблему, что только учитывая целое число, я не могу знать, какой тип объекта он должен идентифицировать. Это было бы здорово сделать во время компиляции.

Решение, которое я предложил с минимальными изменениями, состояло в том, чтобы создать шаблон идентификатора и затем создать для него typedef для различных типов идентификаторов объектов.

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

Я нашел C ++ не считает

typedef int X;
typedef int Y;

Как совершенно разные типы.

Это решение: —

А) разумно (я знаю, что это работает)

Б) Есть ли еще один простой способ сделать это — руководство боится больших изменений LOC

Упрощение решения только с примером оператора.

#include <iostream>

// Horrible old definition of current code
class OldObjectA
{
public:
int ident_; // int identifier
int uniq_;  // another int identifier unique to OldObjectA's only
};

class OldObjectB
{
public:
int ident_;
int next_; // int of another OldObjectB ident_
int uniq_; // another int identifier unique to OldObjectB's only
int dq_;   // int of yet anothera OldObjectB ident_
int com_;  // int of ident_ of a OldObjectA
int ld_;   // int of ident_ of a OldObjectC
};

class OldObjectC
{
public:
int indent_;
int next_; // int of another OldObjectC ident_
int com_;  // int of ident_ of a OldObjectA
};

enum Type { TypeA, TypeAU, TypeB, TypeBU, TypeC };

template<class T, T maxval, Type type>
class ID
{
public:
friend bool operator==(const ID<T, maxval, type> &lhs, const ID<T, maxval, type> &rhs)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
return true;
}
};

typedef ID<int, 42, TypeA>  ID_A;
typedef ID<int, 42, TypeAU> ID_UniqA;
typedef ID<int, 42, TypeB>  ID_B;
typedef ID<int, 42, TypeBU> ID_UniqB;
typedef ID<int, 100, TypeC> ID_C;

// What I was thinking of doing
class NewObjectA
{
public:
ID_A ident_; // int identifier
ID_UniqA uniq_;  // another int identifer
};

class NewObjectB
{
public:
ID_B ident_;
ID_B next_; // int of another OldObjectB ident_
ID_UniqB uniq_; // another int
ID_B dq_;   // int of yet anothera OldObjectB ident_
ID_A com_;  // int of ident_ of a OldObjectA
ID_C ld_;   // int of ident_ of a OldObjectC
};

class NewObjectC
{
public:
ID_C indent_;
ID_C next_; // int of another OldObjectC ident_
ID_A com_;  // int of ident_ of a OldObjectA
};

int main(int argc, char *argv[])
{
std::cout << "================================================================================\n";
ID_A a,a2;
ID_UniqA au,au2;
ID_B b,b2;
ID_UniqB bu,bu2;
ID_C c,c2;

a==a2;
au==au2;
b==b2;
bu==bu2;
c==c2;

// wanted and expected compile time fails
// a=au;
// a=b;
// a=bu;
// a=c;
// au=b;
// au=bu;
// au=c;
// b=bu;
// b=c;

std::cout << "================================================================================\n";return 0;
}

0

Решение

Идея добавления аргументов шаблона для различения идентичных типов в других случаях является разумной. Это полезная техника, которая возникает однажды так часто. Совсем недавно я использовал похожую технику при определении типов для измерений (например, км, литры, секунды и т. Д.).

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

template<class T, T maxval, class tag>
struct ID {
};

template<class T, T maxval, class tag>
bool operator==(ID<T, maxval, tag> lhs, ID<T, maxval, tag> rhs)
{
return true;
}

typedef ID<int, 42, class NewObjectA> ID_A;
typedef ID<int, 42, class NewObjectB> ID_B;

struct NewObjectA {
ID_A id;
};

struct NewObjectB {
ID_B id;
};

void f()
{
ID_A id_a;
ID_B id_b;

id_a == id_a;
id_b == id_b;
//id_a == id_b; // won't compile as expected
}

Преимущество этого состоит в том, что вы не создаете глобальный список всех типов в вашей программе в одном месте. Использование самих типов также может упростить задачу, если вам нужно обрабатывать идентификаторы в соответствии с преобразованиями, разрешенными иерархией наследования. Например, ObjectC является ObjectA, поэтому ID_C можно преобразовать в ID_A.

Я обнаружил, что с добавлением такого рода вещей (и измерений, которые я делаю, похоже), постепенный подход — это, как правило, путь. Каждый раз, когда вам нужно прикоснуться к куску кода, внесите некоторые локальные улучшения и подтвердите, что они работают как положено, прежде чем вносить дополнительные изменения. Тестирование изменений особенно важно, если вы считаете, что обнаружили ошибку и изменили поведение программы. Попытка изменить все сразу, в противоположность этому, часто приводит к большой боли.

1

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

Других решений пока нет …

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