2015-12-23 17 views
8

AFAIKそのための組み込み関数はありません。ウェブ検索私はこれを見つけたfunctionそれは私のために働くが、私はそれがアセンブリであり、私はそれが何をしているのか理解できないので、それを使用しないことを好む。だから私も働き、この機能を書いた:タイプセットの変数の要素数を取得するにはどうすればよいですか?

function Cardinality(const PSet: PByteArray; 
    const SizeOfSet(*in bytes*): Integer): Integer; 
const 
    Masks: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128); 
var 
    I, J: Integer; 
begin 
    Result := 0; 
    for I := 0 to SizeOfSet - 1 do 
    for J := 0 to 7 do 
     if (PSet^[I] and Masks[J]) > 0 then 
     Inc(Result); 
end; 

さて、私はこの機能に頼ることができるかどうかを知りたいですか?または、設定されたデータ型の背後にトリックがあることがあります。そのため、デルファイにはそのための組み込みメソッドがありません。

しかしif私の関数は信頼性があるがthenがどのように私はそれを改善することができます。それに

  1. パス定数
  2. は型チェックを行い、設定が機能
  3. 峠に渡されていることを確認します代わりに、そのアドレス
  4. の値はSizeOfSetパラメータを取り除く

Cardinality(@AnySet, SizeOf(TAnySet))の代わりにCardinality(AnySet)のように呼びたいと思っています。

ところで、私はXEとXE5の両方でこれをコンパイルする必要があります。

答えて

5

これはジェネリックスとRTTIで実装できます。以下のようなので:

uses 
    SysUtils, TypInfo; 

type 
    ERuntimeTypeError = class(Exception); 

    TSet<T> = class 
    strict private 
    class function TypeInfo: PTypeInfo; inline; static; 
    public 
    class function IsSet: Boolean; static; 
    class function Cardinality(const Value: T): Integer; static; 
    end; 

const 
    Masks: array[0..7] of Byte = (1, 2, 4, 8, 16, 32, 64, 128); 

implementation 

{ TSet<T> } 

class function TSet<T>.TypeInfo: PTypeInfo; 
begin 
    Result := System.TypeInfo(T); 
end; 

class function TSet<T>.IsSet: Boolean; 
begin 
    Result := TypeInfo.Kind=tkSet; 
end; 

function GetCardinality(const PSet: PByteArray; 
    const SizeOfSet(*in bytes*): Integer): Integer; inline; 
var 
    I, J: Integer; 
begin 
    Result := 0; 
    for I := 0 to SizeOfSet - 1 do 
    for J := 0 to 7 do 
     if (PSet^[I] and Masks[J]) > 0 then 
     Inc(Result); 
end; 

class function TSet<T>.Cardinality(const Value: T): Integer; 
var 
    EnumTypeData: PTypeData; 
begin 
    if not IsSet then 
    raise ERuntimeTypeError.Create('Invalid type in TSet<T>, T must be a set'); 
    Result := GetCardinality(PByteArray(@Value), SizeOf(Value)); 
end; 

は使用方法:あなたがこれを行うことができますデルファイの以前のバージョンで

Writeln(TSet<SomeSet>.Cardinality(Value)); 
+0

おかげで、しかしXEは、それをコンパイルしません:** E2506インタフェースセクションで宣言されたパラメータ化された型のメソッドは、ローカルシンボル 'GetCardinality' **を使用してはなりません。だから私は 'GetCardinality'をクラス関数として定義しなければなりません。 「マスク」についても同じ問題があります。それらの変更の後、実行され、正常に動作します。 – saastn

+1

これは古いコンパイラのコンパイラの欠陥です。必要に応じて、この機能を手動でインライン化することができます。とにかく、コンセプトは健全です。 –

2

function Card(ASet: TSet): Integer; 
var 
    k: TSetElement; 
begin 
    Result := 0; 
    for k in ASet do Inc(Result); 
end; 
+0

あなたのコードで定義した 'TSet'と' TSetElement'の一般的な型や型はありますか? – saastn

関連する問題