공통 델파이용 벡터 컨테이너

2020.04.14 16:50

김원경 조회 수:808

이 게시글은 Jon Lennart Aasenden   블로그 글을 번역한 것입니다

 

·   본문 링크  

https://community.idera.com/developer-tools/b/blog/posts/vector-containers-for-delphi?fbclid=IwAR1Mg3CdRNzFYNSPf-Q8qfGT-2Yt9_kKEZOxQ3_h2NwX14tivUoZpgjMqP4

델파이용 벡터 컨테이너

 

몇 주 전 C++빌더에 관한 기사를 썼는데, 거기서  C++ 클래스를 처음으로 구현했습니다.

 

이 글에서는 벡터 컨테이너에 대해 다루려고 합니다. 델파이 개발자에게는 익숙하지 않은 C ++의 개념입니다. “벡터”라는 단어를 읽을 때 아마도 3D 그래픽과 행렬 수학에 대해 생각할 것입니다. 그러나 벡터 컨테이너는 완전히 다른 것입니다.

 

벡터 컨테이너는 간단히 말해서 "순서대로 나열되는 값"으로 표시 될 수있는 목록(리스트), 배열, 큐 및 데이터 작업을 단순화하는 제네릭 기반 클래스입니다. 당장은 별로 흥미롭게 들리지 않을 수도 있지만, 계속 읽으시다 보면 그 기능에 놀라실 것입니다.

 

처음에 "벡터"라는 용어가 그러한 근본적인 주제와 완전히 동떨어진 것처럼 보인다는 것을 인정합니다; 하지만 그 단어의 뜻을 구글로 검색하면 벡터는 단순히 방향을 의미합니다. 그리고 포인터는 모든 수단과 목적을 위한 정렬(sort) 표제입니다.

 

델파이에서의 벡터

 

오브젝트 파스칼과 C ++는 기능적인 측면에서 거의 동일하지만 언어와 생태계가 서로 다른 몇몇 측면들이 있습니다.어떤 면에서는 근본적으로 델파이는 C ++ 표준 라이브러리와 똑같은 리포트를 가지고 있지 않을 수도 있지만 그럼에도 불구하고 항상 좋은 대안을 제시해 왔습니다. 델파이와 C++ 빌더는 VCL 및 FMX 프레임 워크를 지원한다는 공통점을 공유하지만 벡터 클래스는 VCL 또는 FMX의 일부가 아니라 C ++ 표준 라이브러리 (std :: vector)입니다. 따라서 델파이에는 실제로 직접 일치하는 것이 없습니다.

 

C++에서 벡터 컨테이너가 매우 유용하다는 것을 알았기 때문에, 델파이용 벡터를 구현하는 것이 어떨까 생각해서 작성하게 되었습니다. 따라서 이 글에서 벡터 목록이 왜 유용한지, 목록을 어떻게 사용하는지, 그리고 마지막으로 자신의 프로젝트에

복사하여 사용할 수 있는 전체 소스 코드(완전히 무료인 공개 소스)에 대해 설명하겠습니다.

 

참고 : 이 글의 마지막에 BitBucket 저장소 링크가 제공됩니다.

 

컨테이너, 빅딜은 무엇입니까?

 

언급했듯이 벡터 클래스는 "데이터 시퀀스"를 처리하는 컨테이너일 뿐이며, 대부분의 경우 목록이나 배열을 의미합니다. 벡터 클래스를 컨테이너라고 부르는 이유는 항목이 할당되거나 저장되는 방식과 관련이 없고 할당자(Allocator)라 불리는 두 번째 클래스와 관련 있기 때문입니다.

 

벡터 클래스(순수하게 시너지 또는 병렬로 간주)에 대해 생각하는 도움이 되는 방법은 TDataset처럼 생각하는 것입니다. TDataset은 데이터의 출처와 배후에서 구성되는 방식을 추상화합니다. 벡터 클래스는 TDataset과 마찬가지로 일련의 데이터를 탐색하는 방법을 제공하지만 세부 정보는 포함하지 않습니다.

 

벡터 목록은 C ++ 거의 모든 곳에서 사용되므로 작성해야 하는 코드의 양을 줄이는 데 도움이됩니다. 배열에서 작동하는 코드가 리스트에서 동일하게 작동합니다. 개발자는 또한 자신의 할당자 인스턴스를 작성할 수 있으며, 디스크에 데이터를 저장하거나 데이터베이스 레코드를 C 구조체에 직접 매핑할 수 있습니다. 특정 시나리오에 할당자를 구현해야 하는 경우 벡터 컨테이너가 이를 나타낼 수 있습니다. 

 

할당자 클래스

 

C++ 표준 라이브러리는 두 개의 할당자 클래스를 제공하며, 두 클래스는 모두 타입이 지정된 데이터와 함께 사용할 수 있는 메모리 내 목록을 나타냅니다.  위에서 언급 한 것처럼 세 번째 할당자 타입 (또는 기술적으로 할당자가 상속하는 기본 클래스)도 있습니다. 요약하면:

  • Sequenced
  • Dynamic
  • Custom

각각 어떤 차이가 있는지 살펴보겠습니다.

 

순차(시퀀싱) 할당자

 

시퀀싱은 아이템 목록이 하나의 연속적인 메모리 블록(아이템이 차례차례 가지런히 정리되어 있는 곳)으로 할당됨을 의미합니다. 델파이에서 가장 가까운 것은 팩 된 레코드 배열입니다. 이 할당자는 간단한 수학을 사용하여 각 항목의 오프셋을 계산할 수 있기 때문에 값을 읽는 데 매우 빠르다는 장점이 있습니다.

 

단점은 이러한 벡터 목록을 채울 때 메모리를 자주 다시 할당해야한다는 것입니다. 저는 캐시 메커니즘을 구현하는 데 시간이 많이 걸리지 않았습니다. 즉, 항목을 추가, 삽입 또는 제거 할 때마다 목록을 보관하는 전체 메모리 세그먼트가 다시 할당됩니다 (다음 개정편에 추가 할 내용).

 

그러나 C ++ 사양은 적절한 캐시로 작동하여 실제로 사용하는 것보다  더 많은 항목을 할당하므로 재할당을 최소한으로 유지합니다. 동일한 용량 및 사용 시스템은 델파이 목록에서도 공통입니다.

 

시퀀싱 된 할당자의 또 다른 이점은 전체 내용을 디스크에 빠르게 덤프하거나 메모리에 복제 할 수 있다는 것입니다. 모든 항목은 결국 단일 메모리 덩어리에 유지 관리되므로 크기와 오프셋을 예측할 수 있습니다 (따라서 단일 작업으로 TFileStream에 데이터를 쓸 수 있음). 두 벡터 목록(할당자 유형이 같은)간의 할당도 마찬가지로 효율적입니다.

 

따라서 일반적인 프로그래밍 작업은 시퀀싱 된 스토리지에서 즉시 이점을 얻지 못할 수도 있지만 벡터 컨테이너가 놀라운 일을 할 수 있는 몇 가지 시나리오가 있습니다.

 

호기심으로 닌텐도 DS와 같은 플랫폼에서 디스플레이는 실제로 파일이라고 언급할 수 있습니다. 따라서 파일 시스템의 파일에 값을 쓰면 그래픽이 그려집니다. C ++ 개발자는 파일을 메모리에 매핑하는 사용자 지정 할당자와 함께 벡터 컨테이너를 사용하여 이 모호한 문제를 매우 우아하게 해결했습니다. 이런 식으로 픽셀을 배열처럼 읽고 쓸 수 있습니다.

 

참고 : 델파이가 니텐도DS를 타겟팅 할 수 있다고 제안하는 것은 아닙니다. 단지 특이한 데이터 표현에 직면했을 때 벡터 컨테이너가 얼마나 유연하고 강력한지를 이야기하는 것입니다.

 

동적 할당자

 

동적은 표준 델파이 배열이 정보를 저장하는 방법과 거의 동일합니다.

 

이  타입은 항목이 메모리에 저장되는 방법에 대한 기준이 없습니다. 할당자가 각 항목을 전달할 수 있는 한, 벡터 컨테이너는 세부 사항에 대해 더 이상 신경 쓸 수 없습니다.

 

이는 항목을 예측 가능한 방식으로 저장하도록 요구하는 시퀀싱 된 모델과는 정반대입니다.

 

델파이에서 동적 할당자와 가장 가까운 것은 일반적인 TArray<> 일 것입니다.

 

사용자 지정 할당자

 

벡터 컨테이너가 순차적 또는 동적 할당자로만 작동한다면, 일반적인 목록이나 컬렉션과 비교하여 그 유용성은 미미할 것입니다. 벡터 컨테이너의 힘은 누구나 자신의 할당자를 구현할 수 있어 인터페이스를 손상시키지 않고 거의 모든 소스나 매체의 데이터 시퀀스로 작업할 수 있다는 것입니다. 여기서 시간을 절약할 수 있는 기능이 제공됩니다.

 

값의 목록이나 순서로 표시해야하는 경우, 그것에 대해 할당자를 작성할 수 있으며, 결과적으로 공통 벡터 인터페이스를 통해 데이터에 접근할 수 있습니다.

 

다음은 구현할 수 있는 할당자에 대한 몇 가지 제안사항입니다. 각자의 상황에 맞는 할당자 클래스를 작성하는 것은 궁극적으로 개발자 각자의 몫입니다.

 

  • 데이터베이스 레코드를 파스칼 레코드에 매핑하는 할당자
  • 디스크 기반 배열과 작동하는 할당자
  • JSON 또는 XML 노드와 작동하는 할당자
  • 메모리 매핑을 사용하는 할당자
  • 디렉토리 목록을 래핑하는 할당자
  • 텍스트 파일을 래핑하는 할당자

 

말할 것도없이 델피의 강력한 RTTI를 방정식에 추가하자마자 흥미로워집니다. 데이터베이스 필드를 레코드에 매핑하는 벡터 목록은 작성하기가 매우 간단합니다. ORM(Object Relational Mapping)에 관한 한, 클래스 인스턴스를 피하고 파스칼 레코드 유형에 직접 매핑하는 것이 더 빠르고 메모리 효율적입니다. 이 자료에서 접해보지 못한 흥미로운 할당자들을 접할 기회가 있을 것 입니다. 

 

예제

 

벡터 클래스 구현을 살펴보기 전에 사용 방법을 살펴 보겠습니다. 순차 할당자는 단일 메모리 블록 내에서 레코드를 유지하는 데 완벽하므로 (낮은 수준의 작업을 처리 할 때 매우 강력 할 수 있음) 이것부터 시작하겠습니다.

 

벡터1.png

 

위의 코드에서 몇 개의 필드를 갖는 간단한 레코드를 정의합니다.  레코드를 초기화하는 함수를 정의합니다. 함수의 구현은 다음과 같이 간단합니다.

 

벡터2.png

 

TVector로부터 상속받는 TMyVectorList라는 클래스를 정의했습니다. 반드시 이렇게 클래스를 정의할 필요 없이 TVector<TTest>의 인스턴스를 만들 수는 있지만, 유틸리티 기능을 가진 벡터 컨테이너를 하위로 확장하려면 이렇게 상속받아 분리하면 코드를 더 쉽게 읽고 사용할 수 있습니다.

 

다음으로 폼 디자이너에서 폼에 TButton과 TListbox를 놓습니다. 이 예제에서 크게 중요하지는 않지만 리스트박스의 앵커 속성을 왼쪽, 위쪽, 오른쪽 및 아래쪽으로 설정하여 폼에 맞게 크기를 조정합니다.

 

벡터3.png

 

화면 디자인이 완료되면 버튼을 더블 클릭하고 다음 코드를 작성합니다.

 

벡터4.png

 

F9 (컴파일 및 실행)로 프로그램을 실행한  버튼을 클릭하면 결과는 다음과 같습니다.

 

벡터5.png

 

 

장점은 무엇입니까?

 

위의 내용이 표준 TArray 또는 TList를 사용하여 동일한 결과를 얻는 것과 무엇이 다른지 궁금하다면 할당자의 작동 방식을 기억해야합니다. 여기서의 차이점은 무엇을 하느냐가 아니라 어떻게 하는가입니다. 벡터 목록의 힘은 레코드 목록을 저장하는 기능이 아니라 출처나 형식에 관계없이 순차적으로 값에 액세스하기 위한 통합 인터페이스를 제공한다는 점을 고려해야합니다.

 

TVector는 목록 데이터의 출처와 방법을 모릅니다. 제네릭 형식(이 예제에서는 TTest)과 목록을 관리하는데 사용 된 할당자만 알고 있습니다. 그 외에도 TVector는 통합 액세스 포인트에 지나지 않습니다.

 

위의 예에서는 순차 할당자를 사용 했으므로 리스트 항목은 단일 메모리 블록 내에 저장됩니다.

 

메모리 무결성이 일관성이 있는지 확인하기 위해, 다시 한번 확인해보겠습니다! 포인터를 사용하여 예전 방식으로 메모리에 직접 접근해보겠습니다!

 

 

원시 데이터에 액세스

 

다음 예제에서는 벡터에 포함 된 원시 메모리 (또는 더 정확하게는 할당자가 제공하고 벡터가 노출하는 원시 메모리)에 액세스합니다. 제가 작성한 벡터 구현은 컨테이너와 할당자가 편리하게 분리되어 인터페이스 참조로만 바인딩됩니다. 그러나 이 인터페이스는 데이터를 활용할 수 있는 방법을 노출 시켰습니다 (물론 해당되는 경우).

 

방법은 다음과 같습니다.

  • LockMemory(var data)
  • UnlockMemory()
  • GetDataSize()

 

벡터 할당자 클래스를 구현할 때 메모리 액세스가 불가능하거나 허용되지 않는 경우 예외 처리를 해야합니다. 컨테이너의 전체 요점은 콘텐츠에 대한 지식을 추상화하는 것이므로  LockMemory() 및 UnLockMemory()를 전혀 포함하지 않습니다. 하지만 솔직히 말해서, 이 모든 것에 대해 실용적이라는 점이 있습니다.

 

폼 디자이너로 돌아와서 다음과 같이 두 번째 버튼을 추가합니다.

 

벡터6.png

 

다음으로 레코드의 포인터 타입을 정의합니다:

 

벡터7.png

 

레코드에 대한 포인터 타입을 정의하면 메모리 주소에서 직접 레코드에 액세스 할 수 있습니다. 이제 추가된 버튼을 더블 클릭하고 다음 코드를 작성하십시오.

 

벡터 8.png

 

 

F9 (컴파일 및 실행)로 프로그램을 실행하고 버튼을 클릭하면 다음과 같은 결과가 표시됩니다.

 

벡터 9.png

 

보시다시피, 시퀀스 할당자는 데이터 무결성을 유지하고 예측 가능한 결과를 제공합니다.

 

열거 가능한 지원

 

원래 C++ 벡터 표준 클래스는 열거자를 지원하지만 열거자를 C++ 표준 라이브러리에서 정의하는 방식은  델파이와 호환되지 않습니다. 따라서 델파이와 호환되지 않는 열거자 패턴을 구현하는 대신 전통적인 델파이 열거자 시스템으로 대신하게 되었습니다. C ++ 벡터 리스트과 100 % 호환되는 것이 좋지만 호환성과 사용가능성 사이에는 약간의 갭이 있습니다. 저는 항상 실용적인 것을 추구하는 편입니다.

 

따라서 TVector 클래스는 TEnumerable에서 상속되므로 다음과 같이 편리하고 현대적인 방식으로 리스트에 액세스 할 수 있습니다.

 

벡터 10.png

 

영속성(Persistence)

 

왜 장점이 많은 TPersistence 대신 TEnumerable에서 상속받은 벡터 목록을 사용하는지 궁금 할 것입니다. 벡터 목록은 표준 Assign() 메서드를 구현하므로 벡터 컨테이너간에 내용을 할당 할 수 있습니다. 또한 할당자 모델이 순차적인지 확인하고 가능한 경우 전체 메모리 블록의 빠른 크기 조정 및 이동을 수행합니다.

 

또한 할당자 기본 클래스가 TInterfacedPersistent에서 상속되므로 나중에 이 부분에서 개선 될 여지가 충분합니다. 그 부분은독자들에게 맡기겠습니다.

 

벡터 클래스 유닛

 

지금까지 충분한 이론과 사용 사례를 다루었으므로 코드를 살펴볼 차례입니다. 

 

코드는 A4 용지로 약 20 페이지에 달하기 때문에 BitBucket에 저장소를 설정해놓았습니다. 이 코드는 MPL v2 (Mozilla Public License)에 따라 릴리스되므로 프로젝트에서 안전하게 사용할 수 있습니다.

 

이곳 저장소에서 다운로드하거나 포크 할 수 있습니다.

 

https://bitbucket.org/cipher_diaz/pasvector/src/master/

 

저장소에는 델파이 및 Freepascal 예제가 모두 포함되어 있습니다. 델파이 지향 에코 시스템에 Freepascal을 포함시키는 것이이상하게 보일 수 있지만, 코드는 TMS 웹 프레임 워크 (Freepascal을 사용하여 델파이 코드를 JavaScript로 변환)를 사용하는 델파이 개발자를 위해 제공됩니다.

 

참고: 버전 1.0.1은 이번 주말(늦어도 월요일)에 출시될 예정이고, 새로운 기능들은 버퍼 지원(형식화되지 않은 메모리 시스템), Views<>(형식화 된배열로 형식화되지 않은 메모리에 액세스) 및 벡터 컨테이너등등  매우 놀랄만한 것들입니다!. 이것들을 결합하면 아주 복잡한 몇 가지 일을 할 수 있습니다. 버퍼 시스템은 메모리와 파일스토리지를 모두를 통합하여 데이터베이스와 같은 디스크에서 벡터 컨텐츠를 직접 사용할 수 있습니다. 자체 버퍼(아마존 또는 Azure 스토리지에서 읽은 버전을 만드는 것은 어떨까?)를 구현할 수도 있습니다.

 

결과적으로 델파이 개발자들은 C++ 개발자들이 수년간 해왔던 것과 같은 편의성과 성능을 누릴 수 있습니다. 새로운 버전은 완벽한 메모리 및 파일 저장, 매핑된 메모리를 도입하고, 유형이 지정된 뷰에서 TBit을 지원하여 C ++ 표준 라이브러리를 능가합니다 (그래서 버퍼를 비트 배열로 탐색 할 수 있습니다!).

 

그러나 위에서 언급 한 시작을 돕기 위해 만들어진 튜토리얼 버전을 먼저 보십시오. 준비가 되면 "최신"하위 폴더에서 코드를 가져와 정말 강력한 기능을 사용하십시오.

 

 

 

번호 제목 글쓴이 날짜 조회 수
공지 [DelphiCon 요약] 코드사이트 로깅 실전 활용 기법 (Real-world CodeSite Logging Techniques) 관리자 2021.01.19 22444
공지 [UX Summit 요약] 오른쪽 클릭은 옳다 (Right Click is Right) 관리자 2020.11.16 20967
공지 [10.4 시드니] What's NEW! 신기능 자세히 보기 관리자 2020.05.27 23041
공지 RAD스튜디오(델파이,C++빌더) - 고객 사례 목록 관리자 2018.10.23 28818
공지 [데브기어 컨설팅] 모바일 앱 & 업그레이드 마이그레이션 [1] 관리자 2017.02.06 30017
공지 [전체 목록] 이 달의 기술자료 & 기술레터 관리자 2017.02.06 25368
공지 RAD스튜디오(델파이, C++빌더) - 시작하기 [1] 관리자 2015.06.30 46310
공지 RAD스튜디오(델파이,C++빌더) - 모바일 앱 개발 사례 (2020년 11월 업데이트 됨) 험프리 2014.01.16 182139
1223 2020년 5월 GM 업데이트 - 여러분의 비즈니스에 길을 열어주세요! 관리자 2020.05.26 345
1222 델파이 안드로이드 개발을 위해 OpenJDK 채택 file 김원경 2020.05.25 962
1221 엠바카데로 오픈 소스 프로젝트 관리자 2020.05.25 800
1220 [10.4 시드니 신기능] 겟잇 패키지 매니저(GetIt Package Manager) 개선 험프리 2020.05.21 655
1219 [10.4 시드니 신기능] 컨트롤 개별 VCL 스타일 적용(Per-Control Style) 적용 험프리 2020.05.19 988
1218 [10.4 시드니 신기능] 새로운 VCL TEdgeBrowser 컴포넌트 험프리 2020.05.18 23174
1217 [10.4] 커스텀 매니지드 레코드(Custom Managed Records) 험프리 2020.05.14 1168
1216 [고객 사례- 솔루션, 델파이] Beyond Compare - 데이터, 시스템 비교/병합/관리 프로그램 관리자 2020.05.14 877
1215 델파이, 25년의 혁신 - 버전 1부터 10.3까지 버전별 핵심 기능 [2] 관리자 2020.05.12 3198
1214 [고객 사례- 솔루션, 델파이] AlignMix - 시각화된 세일즈 관리 도구 관리자 2020.05.12 437
1213 [10.4 시드니 신기능] 다시 태어난 '코드 인사이트' [2] file 험프리 2020.05.08 1799
1212 [발표자료] 20200429 델파이 Push 메시지 전송 시스템 구현 방법 with 구글 Firebase [3] file 관리자 2020.05.04 1119
1211 헬스케어 분야에서의 델파이 - 코로나에 맞서며 file 김원경 2020.04.29 679
1210 [고객 사례- 의료, 델파이] COVID-19 격리 대상자 상태 관리 앱 관리자 2020.04.28 708
1209 이 달의 기술자료 - 2020년 05월 file 험프리 2020.04.24 383
1208 [사례 소개] 데브기어 마이그레이션 유상 컨설팅 사례 험프리 2020.04.22 626
1207 인터베이스와 FireDac에서 배열필드 사용하기 file 김원경 2020.04.16 427
1206 인터베이스, FireDAC 및 TEMSDataSetResource를 사용하여 RAD 서버에서 자동 증가 필드를 생성하는 방법 file 김원경 2020.04.16 457
» 델파이용 벡터 컨테이너 file 김원경 2020.04.14 808
1204 [프로그래밍 애피타이저] 개발이 처음이거나 비 전공자 분들을 위한 가장 첫 번째 STEP! 관리자 2020.04.13 2618