4

私は、動的言語でメインの型として使用されるバリアント クラスを作成しました。これは、最終的に 256 の異なる型の値を許可します (ヘッダーは符号なしバイトで、実際に使用されるのは 20 のみです)。タイプ間のキャスト/変換を実装したいと思います。

私の最初の考えはルックアップ テーブルでしたが、必要となる大量のメモリを実装するのは現実的ではありませんでした。

代替手段は何ですか?現在、研究や他の人からの提案から、さらに 3 つの方法を検討しています。

  1. 数値やコレクションなど、より大きなサブセットに型をグループ化します。
  2. CanCast(from, to) メソッドと Cast(Variant) メソッドを持つ変換インターフェイスを作成し、そのインターフェイスを実装するクラスをリストに追加できるようにします。これにより、変換クラスのいずれかがキャストを実行できるかどうかを確認できます。
  3. (1) と同様ですが、いくつかのマスター タイプを作成します。キャスティングは、元のタイプからマスター タイプへ、そして再び最終的なタイプへの 2 段階のプロセスです。

最適なシステムは何でしょうか?

編集:私はまだ最善のシステムがわからないので、賞金を追加しました.

4

3 に答える 3

1

似たようなことをした。

「ヘッダー」に別のバイトを追加して、実際に格納されているタイプを示すことができます。

C スタイルのプログラミング言語での例:

typedef
enum VariantInternalType {
  vtUnassigned = 0;
  vtByte = 1;
  vtCharPtr = 2; // <-- "plain c" string
  vtBool = 3;
  // other supported data types
}

// --> real data
typedef
struct VariantHeader {
  void* Reserved; // <-- your data (byte or void*)
  VariantInternalType VariantInternalType;  
}

// --> hides real data
typedef
  byte[sizeof(VariantHeader)] Variant;

// allocates & assign a byte data type to a variant
Variant ByteToVar(byte value)
{
  VariantHeader MyVariantHeader;
  Variant MyVariant;

  MyVariantHeader.VariantInternalType = VariantInternalType.vtByte;
  MyVariantHeader.Reserved = value;  

  memcpy (&MyVariant, &MyVariantHeader, sizeof(Variant));

  return myVariant;
}

// allocates & assign a char array data type to a variant
Variant CharPtrToVar(char* value)
{
  VariantHeader MyVariantHeader;
  Variant MyVariant;

  MyVariantHeader.VariantInternalType = VariantInternalType.vtByte;
  MyVariantHeader.Reserved = strcpy(value);  

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariant, &MyVariantHeader, sizeof(Variant));

  return myVariant;
}

// deallocs memory for any internal data type
void freeVar(Variant &myVariant)
{
  VariantHeader MyVariantHeader;

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariantHeader, &MyVariant, sizeof(VariantHeader));

  switch (MyVariantHeader.VariantInternalType) {
    case vtCharPtr:
      strfree(MyVariantHeader.reserved);
    break;

    // other types

    default:
    break;
  }

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariant, &MyVariantHeader, sizeof(Variant));
}

bool isVariantType(Variant &thisVariant, VariantInternalType thisType)
{
  VariantHeader MyVariantHeader;

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariantHeader, &MyVariant, sizeof(VariantHeader));

  return (MyVariant.VariantInternalType == thisType);
}

// -------

void main()
{
  Variant myVariantStr = CharPtrToVar("Hello World");
  Variant myVariantByte = ByteToVar(42);

  char* myString = null;
  byte  myByte = 0;

  if isVariantType(myVariantStr, vtCharPtr) {
    myString = VarToCharPtr(myVariantStr);
    // print variant string into screen
  }

  // ...    
}

これは単なる提案であり、テストされていません。

于 2011-05-06T20:14:02.790 に答える
1

私のシステムは非常に「重い」(大量のコード) ですが、非常に高速で機能が豊富です (クロスプラットフォーム C++)。あなたがデザインをどこまでやりたいかはわかりませんが、私が行ったことの最大の部分は次のとおりです。

DatumState- 「型」の「列挙型」に加えて、を含むすべてのプリミティブ型の「結合」であるネイティブ値を保持するクラスvoid*。このクラスはすべての型から切り離されており、ネイティブ/プリミティブ型、および「参照先」void*型に使用できます。" " は " " と " " のコンテキストenumも持つため、このクラスは a (または何らかのプリミティブ型) を「完全に含む」、または「参照しているが所有していない」a (または何らかのプリミティブ型) として提示できます。(私は実際に " "、" "、および " " コンテキストを持っているので、valuereference-that-c​​annot-be-null を論理的に格納できますVALUE_OFREF_TOfloatfloatVALUE_OFREF_TOPTR_TO

Datum- 完全に を含むクラスですDatumStateが、インターフェイスを拡張して、さまざまな「既知の」型 ( MyDateMyColorMyFileNameなど) に対応します。これらの既知の型は、実際にはメンバーのvoid*内部に格納されます。DatumStateただし、 の " enum" 部分にはDatumState" VALUE_OF" と " " のコンテキストがあるため、" " または " "REF_TOを表すことができます。pointer-to-MyDatevalue-of-MyDate

DatumStateHandle- (既知の) 型 ( 、 、 など) でパラメータ化されたヘルパー テンプレート クラスMyDateMyColorこれは、既知の型から状態を抽出するためにMyFileNameによって使用されるアクセサーです。Datumデフォルトの実装はほとんどのクラスで機能しますが、アクセスに関する特定のセマンティクスを持つクラスは、このテンプレート クラスの 1 つ以上のメンバー関数の特定のテンプレート パラメータ化/実装をオーバーライドするだけです。

Macros, helper functions, and some other supporting stuffDatum- /へのよく知られた型の「追加」を簡単にするために、Variantロジックをいくつかのマクロに集中させ、演算子のオーバーロードなどのサポート関数を提供し、コードに他の規則を確立すると便利であることがわかりました。

この実装の「副作用」として、参照と値のセマンティクス、すべての型での「null」のオプション、すべての型での異種コンテナーのサポートなど、多くの利点が得られました。

たとえば、一連の整数を作成してインデックスを付けることができます。

int my_ints[10];
Datum d(my_ints, 10/*count*/);
for(long i = 0; i < d.count(); ++i)
{
  d[i] = i;
}

同様に、一部のデータ型は、文字列または列挙型によってインデックス付けされます。

MyDate my_date = MyDate::GetDateToday();
Datum d(my_date);
cout << d["DAY_OF_WEEK"] << endl;
cout << d[MyDate::DAY_OF_WEEK] << endl; // alternative

アイテムのセット (ネイティブ) またはセットのDatumアイテム (各アイテムをラップ) を格納できます。どちらの場合でも、再帰的に「アンラップ」できます。

MyDate my_dates[10];
Datum d(my_dates, 10/*count*/);
for(long i = 0; i < d.count(); ++i)
{
  cout << d[i][MyDate::DAY_OF_WEEK] << endl;
}

REF_TO私の " " と " " のセマンティクスはやり過ぎだと主張する人もいるかもしれませんVALUE_OFが、それらは「セットのアンラップ」には不可欠でした。

私はこの " Variant" ことを 9 つの異なるデザインで行いました。私の現在のものは「最も重い」(ほとんどのコード) ですが、私が一番気に入ったもの (オブジェクトのフットプリントがかなり小さく、ほぼ最速) であり、廃止予定です私の使用のための他の8つのデザイン。

私の設計の「欠点」は次のとおりです。

  1. オブジェクトは static_cast<>()a からアクセスされますvoid* (タイプ セーフでかなり高速ですが、間接化が必要です。ただし、副作用として、設計により " null" の格納がサポートされます)。
  2. Datumよく知られている型がインターフェイスを介して公開されるため、コンパイルに時間がかかります (ただし、DatumStateよく知られている型の API が必要ない場合は使用できます)。

デザインに関係なく、次のことをお勧めします。

  1. 「 」などを使用して、「」とは別に「タイプenum」を伝えます。(それらを 1 つの " " またはビット パッキングを使用して圧縮できることは知っていますが、これはアクセスが遅く、新しい型が導入されると維持するのが非常に難しくなります。)int

  2. 型固有の (オーバーライド) 処理のメカニズムを使用して、操作を集中化するためにテンプレートまたは何かに依存します (重要な型を処理する場合)。

ゲームの名前は、「新しいタイプを追加するときのメンテナンスの簡素化」です(または、少なくとも、私にとってはそうでした)。優れたターム ペーパーのように、システムを維持するために必要なコードを常に削除する (たとえば、新しい型を適応させるために必要な労力を最小限に抑える) ため、機能を維持または向上させるために、書き直し、書き直し、書き直しを行うことは非常に良い考えです。既存のインフラストラクチャに)。Variant

幸運を!

于 2011-05-13T04:49:57.990 に答える
0

すでに計算を行っているかもしれませんが、ルックアップテーブルに必要なメモリの量はそれほど多くありません。

タイプに互換性があるかどうかを確認する必要がある場合は、(256 * 256)/2ビットが必要です。これには4kのメモリが必要です。

変換関数へのポインターも必要な場合は、(256 * 256)/2ポインターが必要です。これには、32ビットマシンでは128k、64ビットマシンでは256kのメモリが必要です。低レベルのアドレスレイアウトを実行する場合は、32ビットマシンと64ビットマシンの両方で64kまで下げることができます。

于 2011-05-26T21:10:55.063 に答える