集合型を文字列に変換したり、逆に文字列を集合に変換したいときがあるでしょう。例えば、文字のスタイル(Canvas.Font.Style)を文字列として設定ファイル(*.ini)にセーブしたりロードしたりするときです。 Font.Style のような、オブジェクトの published な集合型のプロパティの場合は TypInfo ユニット中にある SetToString, StringToSet というルーチンで変換できます。しかし、集合型の変数や値を相手にするルーチンは用意されていません。 例えば TTreeView のGetHitTestInfoAtなど、結果を集合で返すメソッドはいろいろありますが、返された集合値を文字列として表示、セーブしようと思っても、簡単にはできません。 しかし、集合型の型情報(TTypeInfo)を利用することで、文字列と集合を相互に変換するルーチンを容易に書くことができます。 注意: 下記ルーチンは要素数32個までの集合に使えます。 |
unit SetUtils; interface uses TypInfo; // 集合→文字列変換 function SetToStr(SetType: PTypeInfo; const SetValue): string; // 文字列→集合変換 procedure StrToSet(SetType: PTypeInfo; str: string; var SetVar); implementation uses Windows, SysUtils; function SetToStr(SetType: PTypeInfo; const SetValue): string; var pd: PTypeData; CompType: PTypeInfo; i: Integer; pi: PInteger; begin pd := GetTypeData(SetType); Assert(pd <> Nil); CompType := pd.CompType^; Assert(CompType <> Nil); pd := GetTypeData(CompType); Assert(pd <> Nil); pi := @SetValue; Result := ''; for i := pd.MinValue to pd.MaxValue do if (pi^ and (1 shl i)) <> 0 then if Result = '' then Result := '[' + GetEnumName(CompType, i) else Result := Result + ', ' + GetEnumName(CompType, i); Result := Result + ']'; end; procedure StrToSet(SetType: PTypeInfo; str: string; var SetVar); var pd: PTypeData; CompType: PTypeInfo; i: Integer; pdw: PDWORD; pw: PWORD; pb: PByte; p: PChar; EnumName: string; ENumValue: Integer; begin pd := GetTypeData(SetType); Assert(pd <> Nil); CompType := pd.CompType^; Assert(CompType <> Nil); pd := GetTypeData(CompType); Assert(pd <> Nil); pdw := @SetVar; pw := @SetVar; pb := @SetVar; case pd.OrdType of otSByte, otUByte: pb^ := 0; otSWord, otUWord: pw^ := 0; otSLong, otULong: pdw^ := 0; end; p := PChar(Str); // '[' と ' ' をスキップ while p^ in ['[',' '] do Inc(p); // ',' ' ' #0 ']' までを要素名として取り出す i := 0; while not (p[i] in [',', ' ', #0,']']) do Inc(i); SetString(EnumName, p, i); // 次の語の先頭までポインタを進める while p[i] in [',', ' ',']'] do Inc(i); Inc(p, i); while EnumName <> '' do begin EnumValue := GetEnumValue(CompType, EnumName); if EnumValue < 0 then raise Exception.Create('集合→文字列 変換エラー'); case pd.OrdType of otSByte, otUByte: pb^ := pb^ or (1 shl EnumValue); otSWord, otUWord: pw^ := pw^ or (1 shl EnumValue); otSLong, otULong: pdw^ := pdw^ or (1 shl EnumValue); end; // ',' ' ' #0 ']' までを要素名として取り出す i := 0; while not (p[i] in [',', ' ', #0,']']) do Inc(i); SetString(EnumName, p, i); // 次の語の先頭までポインタを進める while p[i] in [',', ' ',']'] do Inc(i); Inc(p, i); end; end; end. |
使用例: TreeView の GetHitTestInfoAt の戻り値を文字列で表示します。
procedure TForm1.Timer1Timer(Sender: TObject); var pt: TPoint; t: THittests; str: string; begin pt := Mouse.CursorPos; pt := TreeView1.ScreenToCLient(pt); t := TreeView1.GetHitTestInfoAt(pt.x, pt.y); Memo1.Lines.Add(SetToStr(TypeInfo(THitTests), t)); end; |