0

私は非常に古い(20年以上)非常に大きな(15 KLOC)ライブラリを維持しており、現在整数で識別されているさまざまなタイプのオブジェクトがあります。これは、整数を指定しただけでは、どのタイプのオブジェクトを識別すべきかわからないという問題を引き起こします。これは、コンパイル時に行うと非常に便利です。

最小限の変更で思いついた解決策は、IDテンプレートを作成してから、さまざまなタイプのオブジェクトIDのtypedefを作成することでした。

2つのまったく異なる識別子が同じ基本的なタイプと範囲を持つ可能性があるため、テンプレートに3番目のパラメーターを追加する必要があることに気付きました。

私はC++が考慮していないことを発見しました

typedef int X;
typedef int Y;

全く違うタイプとして。

この解決策ですか:-

A)合理的(私はそれが機能することを知っています)

B)これを行う別の簡単な方法はありますか-経営陣は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;
}
4

1 に答える 1

1

テンプレート引数を追加して、他の点では同一の型を区別するという考えは合理的です。ときどき登場する便利なテクニックです。ごく最近、同様の手法を使用して、測定のタイプ (つまり、km、リットル、秒など) を定義しました。

あなたが持っているものにできる単純化が少なくとも 1 つあります。列挙型は必要ありません。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
}

これには、プログラム内のすべての型のグローバル リストを 1 か所で作成しないという利点があります。継承階層で許可されている変換に沿って ID を処理する必要がある場合は、型自体を使用すると作業が簡単になります。たとえば、ObjectC は ObjectA であるため、ID_C は ID_A に変換できます。

この種のことを追加すると(そして私が行っている測定も同様です)、漸進的なアプローチが一般的に進むべき道であることがわかります。コードに手を加える必要があるたびに、ローカルでいくつかの改善を導入し、期待どおりに動作することを確認してから、さらに変更を加えます。バグを特定し、プログラムの動作を変更したと思われる場合は、変更をテストすることが特に重要です。対照的に、すべてを一度に変更しようとすると、多くの場合、多くの場合、多くの苦痛が生じます。

于 2012-10-09T23:10:17.320 に答える