2012-10-09 3 views
0

현재 정수로 식별되는 다양한 유형의 개체가있는 매우 오래된 (20 년 이상) 대규모 (15 개 KLOC) 라이브러리를 유지 관리하고 있습니다. 이것은 정수를 주었을 때 어떤 유형의 객체인지 식별 ​​할 수 없다는 문제점을 제기합니다. 이것은 컴파일 타임에 정말 좋을 것입니다.최소한의 변경으로 이전 코드에서 형식 기반 식별 번호 만들기

최소한의 변경으로 해결할 수있는 해결책은 ID 템플릿을 만든 다음 다양한 유형의 개체 ID에 대해 ID 템플릿을 만드는 것입니다.

두 개의 완전히 다른 식별자가 동일한 기본 유형과 범위를 가질 수 있으므로 템플릿에 세 번째 매개 변수를 추가해야한다는 것을 알았습니다.

나는 C++이

typedef int X; 
typedef int Y; 

으로 완전히 다른 유형을 고려하지 않습니다 발견했다. 관리 높은 LOC 무서워의

단순화를 변경 - 합리적인

A) (나는 그것이 작동 알고)

B)이 일을 다른 간단한 방법이 있나요 - :

이 솔루션인가 솔루션을 예로 든다.

#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; 
} 

답변

1

틀린 유형을 구별하기 위해 템플릿 인수를 추가하는 것이 합리적입니다. 한 번씩 수확하는 유용한 기술입니다. 가장 최근에는 측정을위한 유형을 정의하는 데 비슷한 기술 (예 : km, 리터, 초 등)을 사용했습니다.

당신이 갖고있는 것에 적어도 하나의 단순화가 있습니다. 열거 형은 필요하지 않습니다. ID 정의에서 유형 자체를 사용하여 도망 갈 수 있습니다.

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 
} 

이렇게하면 프로그램의 모든 유형에 대한 전체 목록을 한 곳에서 만들지 않아도됩니다. 유형 자체를 사용하면 상속 계층 구조에서 허용하는 변환에 따라 ID를 처리해야하는 경우 작업을 더 쉽게 수행 할 수 있습니다. 예를 들어, ObjectC는 ObjectA이므로 ID_C는 ID_A로 변환 가능합니다.

나는 이런 종류의 것을 추가하고있다. (그리고 나는 측정하고있는 것들도 비슷하다.) 점진적 접근 방식은 일반적으로 갈 길이 멀다. 코드를 터치해야 할 때마다 로컬에서 몇 가지 개선 사항을 소개하고 변경 사항을 적용하기 전에 예상대로 작동하는지 확인합니다. 변경 사항을 테스트하는 것이 버그를 식별하고 프로그램 동작을 변경했다고 생각하는 경우 특히 중요합니다. 대조적으로 모든 것을 한꺼번에 바꾸려고 할 때 종종 고통을 많이 겪습니다.