Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 팁&트릭
Delphi Programming Tip&Tricks
[263] TStrings를 Variant로 저장해보기... Variant의 적극 활용
DrK [whitekid] 9486 읽음    2008-12-09 15:37
블로그의 글을 옮겨 봅니다.

델파이에서 기본으로 제공하는 Variant 타입은 여기다가 Integer, String, Double... 등등 기본적인 타입을 모두 넣을 수 있으며 활용 여하에 따라서는 굉장히 유용하게 사용할 수 있습니다. OLE 같은 부분도 이런 Variant를 많이 사용하고 데이터베이스 필드 변수같은 경우도 Variant를 사용하는 것을 볼 수 있습니다.

Variant가 데이터베이스 필드 변수로서 특히나 매력적인 부분은 일반 변수에서는 표현할 수 없는 Null 데이터를 표현할 수 있다는 것입니다. (TField, TParam을 사용하는 사람에게는 이상하게 들릴지도.. 하여간에..)

그러다가 오늘 갑자기 내가 사용하는 객체를 Variant로 넣는다면 하는 생각이 들었습니다. 그래서 테스트로 TStrings를 Variant로 만드는 것을 해 봤는데, 델파이 도움말을 찾아서 하나씩 하나씩 진행해봤습니다. 우선 최종적으로 완료가 되면 성공하는 테스트케이스는 다음과 같습니다. 나머지 TStrings의 메써드 및 프러퍼티는 필요에 따라서 넣으면 되겠지요.

총 필요한 것은 레코드 타입 클래스(TStringVarType), 실제 데이터를 표현하는 클래스(TStringData), Variant 데이터(TStringsVarData), 그리고 마지막으로 타입을 만드는 유틸리티 함수(TStringVarType.VarCreate)로 이루어 집니다.

procedure TestTCustomVariantType.TestVariant;
var
  V: Variant;
  L: TStrings;
  S: String;
begin
  L := TStringList.Create;
  L.Text := 'TEST';
  S := L.Text;
  try
    V := TStringsVarType.VarCreate(L);
    CheckEquals(S, V.Text);
  finally
    L.Free;
  end;

  CheckEquals(S, V.TEXT);
end;


Variant 타입으로 맵핑될 TStringVarType을 쓰려면은 TCustomVariantType에서 상속을 받아야 하지만, 나중에 Property나 Method가 접근가능해야 하므로 TInvokeableVariantType에서 상속을 받아서 사용합니다. 우선은 비워둡니다. 나중에 하나씩 채워가지요.

TStringsVarType = class(TInvokeableVariantType)
end;

클래스 선언은 끝났고, 다음 과정은 이 타입이 선언된 initialization section에서 이 타입을 변수를 하나 만들어 줍니다.

var
  StringsVarType: TStringsVarType; 

......

initialization
  StringsVarType := TStringsVarType.Create;

finalization
  StringsVarType.Free;

이렇게 타입 변수를 만들어 주면, 새로 만든 타입에 대한 Type ID(StringVarType.VarType로 설정됨)가 만들어집니다.

다음 과정을 이 변수를 Variant로 만드는 helper function을 만드는 것인데요. 테스트케이스의 TStringVarType.VarCreate 함수입니다. 이를 통해서 실제로 TStrings의 변수를 variant로 변환합니다.
class function TStringsVarType.VarCreate(S: TStrings): Variant;
begin
  VarClear(Result);
  with TStringsVarData(Result) do
  begin
    VType := StringsVarType.VarType;
    VData := TStringData.Create(S);
  end;
end;


TStringVarData는 TStringVarType과 실제 데이터를 열결해주는 레코드입니다. 거기에다가 VarType과 실제 데이터를 저장하는 객체를 연결시켜주면 됩니다. Variant가 객체를 포함할 경우는 아래의 형식을 따라하기 바랍니다. 도움말에 의하면 전체 레코드의 크기가 14byte를 넘으면 아래의 형식대로 하고 아니면 다른 형식대로 하라고 되어있습니다. 그렇게 하지 않고 해봤더니 컴파일시에 레코드 타입 케스팅 오류가 납니다. 특별한 이유가 없다면 그대로 가면 되겠습니다.
TStringsVarData = packed record
  VType: TVarType;
  Reserved1, Reserved2, Reserved3: Word;
  VData: TStringData;
  Reserved4: LongInt;
end;

VData에 들어가는 TStringData는 별거없고 constructor로 넘겨진 TStrings 변수를 내부에서 Assign해서 하나의 변수로 가지고 있을 뿐입니다. TStringVariantType.GetProperty에서 접근하는 곳이 이 클래스 입니다.

여기까지 하면 컴파일 하면 오류가 나지 않습니다. 몇가지 오류가나는데 TStringVarType에 Clear, Copy는 Abstract라고 합니다. 이건 override해서 우선은 빈 함수로 나두고 컴파일하면 이제 경고없이 잘 컴파일 될 겁니다. 실행하면 오류가 나는데 V.TEXT 프러퍼티를 접근하면서 런타임 에러를 냅니다.

이제 마지막 과정인 실제 property를 접근할 차례인데 TStringVarType에 TInvokeableVariantType에 선언된 GetProperty를 override해서 다음처럼 만들어 줍니다.

function TStringsVarType.GetProperty(var Dest: TVarData; const V: TVarData;
  const Name: string): Boolean;
begin
  Result := True;
  if Name = 'TEXT' then
    Variant(Dest) := TStringsVarData(V).VData.Strings.Text
  else
    Result := False;
end;


설명하지 않아도 코드만 보면 알수 있을 겁니다. 이런 식으로 해서 프러퍼티를 접근합니다. function/ procedure도 TInvokeableVairantType에 있는 DoFunction, DoProcedure를 override해서 사용하면 됩니다. 물론 property setter도 SetProperty를 override해서 사용하면 되겠지요.

이걸로 TStrings를 Variant로 만드는 과정이 다 끝났군요. 전체 소스는 다음과 같습니다.

unit TestCustomVariantType;

interface

uses
  TestFramework;

type
  TestTCustomVariantType = class(TTestCase)
  {$M+}
  published
    procedure TestVariant;
  end;

implementation

uses
  Classes, Variants, SysUtils;

type
  TStringsVarType = class(TInvokeableVariantType)
  public
    procedure Clear(var V: TVarData); override;
    procedure Copy(var Dest: TVarData; const Source: TVarData; const Indirect: Boolean); override;
    function GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean; override;

    class function VarCreate(S: TStrings): Variant;
  end;


  TStringData = class
  private
    FString: TStrings;
  public
    constructor Create(S: TStrings);
    destructor Destroy; override;

    property Strings: TStrings read FString;
  end;


  TStringsVarData = packed record
    VType: TVarType;
    Reserved1, Reserved2, Reserved3: Word;
    VData: TStringData;
    Reserved4: LongInt;
  end;

var
  StringsVarType: TStringsVarType;


{ TestTCustomVariantType }

procedure TestTCustomVariantType.TestVariant;
var
  V: Variant;
  L: TStrings;
  S: String;
begin
  L := TStringList.Create;
  L.Text := 'TEST';
  S := L.Text;
  try
    V := TStringsVarType.VarCreate(L);
    CheckEquals(S, V.Text);
  finally
    L.Free;
  end;

  CheckEquals(S, V.TEXT);
end;

{ TStringsVarType }

procedure TStringsVarType.Clear(var V: TVarData);
begin
  inherited;

  // todo
end;

procedure TStringsVarType.Copy(var Dest: TVarData; const Source: TVarData;
  const Indirect: Boolean);
begin
  inherited;

  // todo
end;

function TStringsVarType.GetProperty(var Dest: TVarData; const V: TVarData;
  const Name: string): Boolean;
begin
  Result := True;
  if Name = 'TEXT' then
    Variant(Dest) := TStringsVarData(V).VData.Strings.Text
  else
    Result := False;
end;

class function TStringsVarType.VarCreate(S: TStrings): Variant;
begin
  VarClear(Result);
  with TStringsVarData(Result) do
  begin
    VType := StringsVarType.VarType;
    VData := TStringData.Create(S);
  end;
end;

{ TStringData }

constructor TStringData.Create(S: TStrings);
begin
  inherited Create;

  FString := TStringList.Create;
  FString.Assign(S);
end;

destructor TStringData.Destroy;
begin
  FreeAndNil(FString);
  inherited;
end;

initialization
  RegisterTest(TestTCustomVariantType.Suite);
  StringsVarType := TStringsVarType.Create;

finalization
  StringsVarType.Free;

end.


ps. 항상 느끼는 거지만 글을 쓸는건 정말 어렵군요. 머릿 속에는 뭔가 많은게 있는데 이 표현력의 한계란...
</p>

+ -

관련 글 리스트
263 TStrings를 Variant로 저장해보기... Variant의 적극 활용 DrK 9486 2008/12/09
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.