TCollection, TCollectionItem 에서 파생시켜 리스트를 만들고 TCollectionItem 에 이벤트 속성을 넣고 개발시에 이벤트를 생성하려고 하면 오류가 난다. 기존 작성된 메소드 연결은 되는데 생성시만 "Cannot create a method for an unnamed component" 메세지와 함께 오류가 난다.
원인은 TCollection 에서 GetNamePath 를 호출 할 때 부모 클래스 이름이 없기 때문이다. GetOwner 라는 메소드에서 Owner 객체가 반환 되어야 하는데, 기본적으로 TPersistent 의 메소드에서 nil(NULL) 을 반환한다.
해결 방법은 Owner 를 기억하고 있다가 GetOwner 메소드로 반환시키면 된다. 이를 구현해 놓은 클래스가 있는데 TOwnedCollection 이다. TCollection 부분을 TOwnedCollection 으로 고치면 잘 동작할 것이다.
function XOR(I, J: Integer): Integer; var K: Integer; begin Result := 0; K := 1; while I <> J do begin if odd(I) <> odd(J) then Inc(Result, K); I := I shr 1; J := J shr 1; K := K shl 1; end; end;
function DayOfWeek(Y, M, D: Integer): Integer; begin if M < 3 then begin Dec(Y, 1); Inc(M, 12); end; Result := (Y + (Y shr 2) - (Y div 100) + (Y div 400) + ((13 * M + 8) div 5) + D) mod 7; end;
function Fib(N: Integer): Integer; begin Result := Round(exp(N * Ln((1 + sqrt(5)) / 2)) / sqrt(5)); end;
// 정수형 SquareRoot. sqrt 와 비슷하거나 약간 빠르나 정수만 가능하다. function sqrtInt(X: Integer): Integer; var Y: Integer; begin Result := 1; repeat Y := Result; Result := (X div Result + Result) div 2; until (Result = Y) or (Result = X div Y); end;
// 실수형 SquareRoot. sqrt 보다 많이 느리다. (약 4~5배) function sqrt2(X: Real): Real; begin Result := exp(ln(X) / 2); Result := (Result + X / Result / 2); end;
080425 - 문자 삭제에 따른 이전 '-' 위치 값 변경 수정 ExtractFloat 추가
숫자에 사용되는 문자('-', '.') 에 대한 처리가 되어있다.
일반적인 변환에 관해서는 StrToInt 가 더 빠르게 동작하므로, 예외 상황이 가끔 발생하는 경우 TryStrToint 를 이용한 뒤 변환되지 않은 경우 적용하면 된다.
// Int64 의 경우 반환 형태만 Int64 로 바꾸면된다. function ExtractInteger(Str: String): Integer; var I: Integer; H: Integer; begin H := 0; for I := Length(Str) downto 1 do begin if ('0' <= Str[I]) and (Str[I] <= '9') then begin if H > I then begin Delete(Str, H, 1); H := 0; end; end else if Str[I] = '.' then begin Delete(Str, I, Length(Str) - I + 1); if H > 0 then Dec(H); end else if Str[I] = '-' then H := I else begin Delete(Str, I, 1); if H > 0 then Dec(H); end; end; Val(Str, Result, H); end;
// __int64 의 경우 반환형태를 __int64 로 바꾸고, 반환시 StrToInt64 로 변환하면 된다. int __fastcall ExtractInteger(String Str) { int H = 0; for (int i = Str.Length() + 1; --i; ) { if ('0' <= Str[i] && Str[i] <= '9') { if (H > i) { Str.Delete(H, 1); H = 0; } } else if (Str[i] == '.') { Str.Delete(i, Str.Length() - i + 1); if (H > 0) H--; } else if (Str[i] == '-') H = i; else { Str.Delete(i, 1); if (H > 0) H--; } } return StrToInt(Str); }
// ExtratInteger 에 '.' 위치에 따른 처리를 넣었다. function ExtractFloat(Str: String): Extended; var I: Integer; H: Integer; D: Integer; begin H := 0; D := 0; for I := Length(Str) downto 1 do begin if ('0' <= Str[I]) and (Str[I] <= '9') then begin if H > I then begin Delete(Str, H, 1); H := 0; if D > 0 then Dec(D); end; end else if Str[I] = '.' then begin if D > 0 then begin Delete(Str, D, 1); if H > 0 then Dec(H); end; D := I; end else if Str[I] = '-' then begin H := I end else begin Delete(Str, I, 1); if D > 0 then Dec(D); if H > 0 then Dec(H); end; end; TextToFloat(PChar(Str), Result, fvExtended); end;
// ExtratInteger 에 '.' 위치에 따른 처리를 넣었다. Extended __fastcall ExtractFloat(String Str) { int H = 0, D = 0; for (int i = Str.Length() + 1; --i; ) { if ('0' <= Str[i] && Str[i] <= '9') { if (H > i) { Str.Delete(H, 1); H = 0; if (D > 0) D--; } } else if (Str[i] == '.') { if (D > 0) { Str.Delete(D, 1); if (H > 0) H--; } D = i; } else if (Str[i] == '-') H = i; else { Str.Delete(i, 1); if (H > 0) H--; if (D > 0) D--; } } return StrToFloat(Str); }
DefineProperties 메소드를 구현해주면 된다. TFiler 를 매개변수로 받고 DefineProperty 메소드를 이용해서 Load, Write, CanDo 메소드를 인자 값으로 넣어준다.
Load 메소드의 인자 값은 TReader 형태 Write 메소드의 인자 값은 TWriter 형태 CanDo 메소드의 인자 값은 TFiler 형태로 받고
CanDo 에서는 Filer->Ancestor 와 비교해서 가능 여부를 반환하고 나머지는 각 인자 값으로 넘어온 객체를 이용해 Load/Write 하면 된다. -------------------------------------------------------------------
복합적인 Property 의 경우 Binary 로 저장이 가능하다. Binary 저장은 다음과 같다.
배열로 잡혀진 객체를 저장하는데 이 코드를 썼는데, 객체에 Read/Write 메소드를 구현하여, ReadCells, WriteCells 에서는 이를 호출만 하게 해주면된다.
객체의 Read/Write 메소드 구현은 파일 저장과 같다. TStream 을 받아서 저장을 하게 되는데 TTreeView 의 경우는 자료 저장용 구조체를 따로 만들어 값을 넣고 Read/Write 를 한다.
이 때, 주의할 점은 문자열 처리다. 문자열은 가변길이이기 때문에 구조체의 맨 마지막에 와야한다. Delphi 의 string 과 C++Builder 의 AnsiString 은 다른 형태라 같은 루틴을 사용할 수 없다. Delphi 의 코드를 보면 255 크기의 string 변수를 생성하고 값을 대입하고 크기를 구해서 크기와 값을 저장하고 읽어온다. C++Builder 에서는 클래스를 바로 저장할 수 없기에 (포인터 값이 저장되어 난리난다.) char [255] 배열을 생성해서 비슷한 구현을 하면된다. 다만 Write 시에는 strncpy 로 값을 복사해 줘야 하지만, Read 시에는 그냥 대입하면 자동 String 변환이 된다.
typedef struct _DIV { union { struct { int Mod2: 1; int Div2: 7; }; struct { int Mod4: 2; int Div4: 6; }; struct { int Mod8: 3; int Div8: 5; }; struct { int Mod16: 4; int Div16: 4; }; struct { int Value; }; }; } TDiv;
사용법 1. 직접 대입 방법 TDiv A; A.Value = 6; int Div16 = IntToStr(A.Div16); int Mod16 = IntToStr(A.Mod16 & 0x0F);
2. int 변수를 형변환 방법 int A = 6; int Div16 = IntToStr(((TDiv*)&A)->Div16); int Mod16 = IntToStr(((TDiv*)&A)->Mod16 & 0x0F);