공통 델파이 개발자 관점에서 본 C++ 빌더
2020.07.20 16:32
이 글은 Jon Lennart Aasenden의 블로그 글을 토대로 번역한 것입니다.
· 원문 링크
델파이 개발자 관점에서 본 C++ 빌더
델파이가 출시 된 지 2 년 만에 C ++ 빌더가 탄생했습니다. 그때까지 델파이는 사용하기 쉬운 설계 툴과 훌륭하게 엔지니어링 된 VCL 컴포넌트 프레임 워크를 통해 다른 툴과의 경쟁에서 우위를 차지하고 있었습니다.
C ++ 빌더의 출시로 C 개발자들은 마침내 델파이와 동일한 빠른 애플리케이선 개발(RAD) 방식을 누릴 수 있게 되었습니다. 그 당시 델파이를 사용하던 저는 이것이 C/C++에게 얼마나 엄청난 도약이었는지를 아직도 기억하고 있습니다. 볼랜드 사가 모든 전력을 쏟아부어 만들어 낸 결과물이었습니다. 왓콤이나 스톰C를 사용해왔던 개발자들은 놀랄 수 밖에 없었습니다: 오늘날 툴체인은 IT역사의 기록에서 부차적인 부분입니다.
델파이 개발자를위한 C ++
델파이와 C ++ 빌더에 대해 사람들을 놀라게하는 것 중 하나는 이 두 제품이 한때 상당량의 동일한 컴파일러 기술을 공유했다는 것입니다. 언어 계층이 제품 간의 유일한 차이점이었습니다.
이제 언어와 런타임 동작 간의 중요한 차이점을 지적하기 전에 이 제품들이 볼랜드하에서 얼마나 밀접하게 결합되어 있는지에 대해 이야기하고 있습니다. 이진 호환성은 한동안 볼랜드의 목표 중 하나였습니다.
그 이후로 언어와 제품 양쪽의 내부 역학이 분분해졌지만, 두 언어는 나란히 진화했고, 풍부한 역사를 함께 공유했습니다.
대부분의 델파이 개발자들처럼 저도 C++빌더에 대해 더 많이 배우고 언어를 사용해 보고싶다고 자주 느껴왔습니다. 결국, 제품과 컴파일러 모두 친숙하게 알고 있는 프레임워크인 VCL을 활용하며, 멀티플랫폼을 위한 새롭고 훌륭한 파이어몽키 프레임워크를 활용합니다.
그래서 만약 제가 다른 언어를 배운다면, 당연히 C++ 빌더를 자연스럽게 선택일 것입니다.
델파이 개발자를위한 C++ 빌더의 장점
기본적으로 C ++ 빌더는 다음과 같은 장점을 제공합니다.
- 사용했던 IDE와 동일합니다.
- 폼 디자이너
- 키보드 단축키
- 디버거 및 브레이크포인트
- 패키지 시스템은 동일합니다.
- 이미 알고있는 VCL을 사용할 수 있습니다.
- 파이어몽키는 멀티 플랫폼 애플리케이션 용 입니다.
이러한 친숙함 외에도 델파이 애플리케이션에 더 넓은 범위를 제공하는 보다 기술적이고 저수준(low-Level) 특성의 장점이 있습니다.
- C / C ++ 전용의 자료에 액세스
- 델파이 프로젝트에 링크 될 수있는 .obj 파일로 라이브러리를 컴파일하는 기능
- 델파이와 호환되는 컴포넌트 및 패키지를 작성하는 기능
- 델파이 또는 DLL 라이브러리를 로드할 수 있는 모든 언어)에서 C/C++ 전용 프레임워크를 사용할 수 있도록 하는 프록시 라이브러리 작성
- LLVM 기반 코드 생성 및 최적화를 최대한 활용하십시오.
LLVM 기반 코드 생성의 마지막 포인트는 확실히 저에게는 판매 포인트입니다. 델파이는 모바일 타겟에서 LLVM 컴파일 이진 파일을 즐겨 사용하지만, 데스크톱 애플리케이션(macOS, x86 리눅스 포함)은 여전히 전통적인 델파이 컴파일러로 컴파일됩니다.
전통적인 델파이 컴파일러는 매우 강력하고 잘 작성되어 있지만, LLVM은 수백 명의 컴퓨터 과학자와 협력하여 해당 분야의 최고를 차지하고 있습니다. 컴파일러 기술에 관한 한, 클랑(LLVM)은 어떤 단일 기업이 제공할 수 있는 것보다 훨씬 많은 양의 작업과 정교함을 제공합니다. LLVM은 학계및 업계 전반의 재정 지원을 받고 있으며, 이 연구가 일리노이 대학에서 시작되었지만, 오늘날에는 자체 조직에 의해 관리되고 있습니다.
델파이는 제가 가장 좋아하는 언어 및 개발 플랫폼으로 계속 사용되고 있습니다. 어떤 언어라도 델파이를 대체할 수 있다고 생각하지 않습니다. 델파이는 속도, 기능, 그리고 맞추기 어려운 생산성 사이에서 완벽한 균형을 이룹니다.
그러나 C/C++등이 더 나은 결과를 산출할 수 있는 상황이 있는데, 특히 이진 데이터 처리하고 조작할 때 더욱 그렇습니다.
C ++는 C가 아닙니다.
이전 세기로 거슬러 올라가면, 저는 바닐라 C를 꽤 많이 사용했습니다. 주로 성능이 중요한 공유 라이브러리에 사용됩니다.
저의 유일한 선택은 어셈블러에 의존하는 것이었습니다. 제 블로그를 팔로우하거나 소셜 미디어에 자주 참여하는 사람들은 십대 시절에 현재 빈티지 Commodore Amiga 컴퓨터에서 상용 소프트웨어를 쓰기 시작했다는 것을 알고 있습니다:제가 여전히 취미로 여가 시간에 코딩하는 것을 즐기는 시스템입니다.
80 년대 후반부터 90 년대 초반에 사용했던 C의 단점은 오늘날 우리가 당연하게 여기는 많은 표준을 아직 발전시키지 못했다는 것입니다. 라이브러리는 대부분 절차적이었기 때문에 그 당시에는 객체 지향에 전혀 신경 쓰지 않았습니다.
그리고 OOP가 그 플랫폼에서 표준이 되었을 때, 나는 이미 파스칼에 빠졌고, x86과 터보 파스칼로 이어졋고 1995년에 델파이가 되었습니다.
25 년 전부터 C++ 빌더에 관심을 갖게되었습니다.
우선 실용적인 이유 때문이기도 하지만, C/C++가 (리눅스상의 임베디드 및 IoT 프로젝트에서 계속 발생하는 미묘한 측면) 독특한 지적 측면을 가지고 있기 때문이기도 합니다. 오브젝트 파스칼로 해결할 수 없는 것은 없지만, “하나의 언어가 모든 언어를 자배할 수는 없습니다.” 개발자들은 자기가 주로 사용하는 툴이나 언어를 잘 다룰 수 있을 뿐만 아니라 다른 언어도 배우는 것이 좋습니다.
따라서 C가 주요 언어라면 어셈블리와 델파이 학습은 좋은 선택입니다. 델파이가 주요 언어라면 C / C++와 자바스크립트를 배우는 것이 좋은 생각 일 것입니다 (자바 스크립트가 델파이의 다음 진화 단계라고 생각하지는 않습니다). 그러나 시장 관점에서 보면 이치에 맞습니다. 델파이 내에서 어셈블리 코딩을 즐긴다면 더 큰 날개를 다는 것입니다.
많은 C ++ 빌더 예제와 리소스를 보면서 깨달은 한 가지는 C++은 실제로 C가 아니라는 것입니다. 학습을 위해 C++ 언어에 관한 책 두 권을 샀는데, 레코드(구조), 포인터, for-Next 루프, 데이터 유형, 변수 등 전통적인 구조에 대한 소개를 기대했던 책에서 C++이 거의 별개의 새로운 언어처럼 다가왔습니다.
델파이는 기능면에서 C/C++와 동일하지만 모든 대상에 대해 LLVM을 명확히 사용할 때까지는 C++빌더가 어느 정도 속도 우위를 점하게 됩니다.
나의 첫 C ++ 클래스
저의 첫 C ++ 클래스는 간단한 클래스로 단순한 메모리 버퍼 클래스였습니다. 단지 표준 메모리 할당을 관리하는 클래스로, 원시 데이터에 쉽게 접근할 수 있는 간단한 메소드와 속성을 가지고 있습니다.
실제로 델파이에 구현 된 이러한 버퍼 클래스의 훨씬 정교한 라이브러리가 있습니다. 이러한 기능은 더욱 발전하여 메모리, 파일 및 클라우드 스토리지를 통합하는 데 도움이되므로 공통 인터페이스를 통해 위치 나 매체에 관계없이 원시 데이터에 액세스 할 수 있습니다. 이러한 클래스에는 insert () 및 remove ()와 같은 메소드가 포함되어 버퍼를 적절하게 늘리고 줄이며 후행 데이터를 파일 내에서 위 또는 아래로 이동합니다 (이전에 TStream에 추가해야했던 것).
이 델파이 클래스는 실제로 데이터베이스 엔진의 기초이므로 “자신만의 데이터베이스 엔진을 코딩하는 방법”에 대한 C/C ++ 튜토리얼은 앞으로 다룰 수 있는 부분입니다.
실습 코딩의 목적
이 코딩 목적은 단순히 C++빌더 환경이 얼마나 멋진지 보여주기 위한 것이며, C++를 배우는 것이 어렵지 않다는 것입니다.
하지만 이글에서는 잘 알고 있지 않은 부분을 도전적으로 시험해 보도록하겠습니다. 델파이 기술이 C++ 빌더를 더 빨리 다루는 데 도움이됩니까? 엠바카데로가 주장하는 것처럼 C++ 빌더를 사용하기가 쉽습니까? 이제 알아 보도록 하겠습니다 !
c++ 사용 경험
이미 RAD Studio 10.3을 설치했지만, 사실 RAD Studio의 C++ 부분을 사용해 본 적은 없습니다. 가끔 폰트 렌더링 엔진 FreeType과 같은 C 라이브러리를 컴파일하고, 그런 다음 생성된 .obj 파일을 사용하는 델파이 그래픽 라이브러리(나중 기사에서 볼 수 있는 프로세스)에 정적으로 링크한 적은 있습니다.
- C/C++
- Object Pascal
언어를 설명하는것은 이 글의 범위를 벗어나지만 언어를 간단하게 정리하도록 하겠습니다.
원형어
즉, 언어가 하드웨어와 직접 인터페이스되도록 설계되었으며 언어가 별도의 런타임 시스템 (예 : 자바 또는 .Net)에 종속되지 않는 코드를 생성 할 수 있음을 의미합니다. 또한 운영 체제에 구속되지 않는 원칙으로 작동합니다.
컨텍스트 기반 언어
두 번째 언어 유형은 종종 낙관적(Optimistic)이라고합니다. 데이터베이스에서 비롯된 용어로, 특정 조건이 이미 충족되었다는 것을“낙관적 잠금(optimistic locking)”으로 간주합니다. 이들은 기존 인프라 (예 : 운영 체제 또는 런타임 환경) 외부에서 작동 할 수 없기 때문에 컨텍스트 기반 언어라고도합니다. C#, 자바, 자바스크립트, 파이썬 등은 모두 컨텍스트 기반 언어입니다.
참고 : Rust 언어를 원형어 목록에 추가합니다. Rust은 C/C ++ 언어의 개혁을 대표한다고 주장 할 수 있는데, 이는 Bjarne Stroustrup의 목표와는 근본적으로 다른 방향으로 움직입니다. 리눅스 커뮤니티는 Rust로 작성된 드라이버를 허용하지 않기 때문에 커널 개발에 대한 Rust의 적합성은 아직 해결되지 않았습니다.
새로운 언어에 접근하는 방법
인간의 마음은 새로운 상황에 직면 할 때 항상 친숙함을 찾을 것입니다. 그리고 그것을 찾을 수 없다면-연관된 하나를(연관적 신화라고 불리는)마음으로 구성 할 수도 있습니다.
이것이 제가 코딩에 들어가기에 몇 시간 동안 접근 방식을 발전시키는 이유입니다. 왜냐하면 방향성이 없으면 진행이 느려질 수 있기 때문입니다.
가장 실용적인 접근 방식은 델파이에서 어떻게 일을 할 것인가를 상상하고, 사용할 주요 언어 요소를 분리시킨 다음, C++ 등가물을 찾는 것입니다. 그래서 몇 시간 동안 필수 사항을 살펴보고 다음과 같이 요구 사항을 좁혔습니다.
- .cpp 파일과 .h 파일의 관계
- 클래스를 정의하는 방법과 장소
- 생성자와 소멸자
- 게터와 세터로 속성을 정의하는 방법
- 포인터 다루기
- 특히“pointer of”와 “pointer to”의 차이점
- 예외 던지기 및 잡기
- 표준 연산자 (and, or, not)
- 표준 조건부 ((if, then, else, case / switch))
C++빌더는 100% 표준규격을 준수하기 때문에 구글은 많은 양의 학습 자료를 제공했습니다(따라서 내가 산 비싼 책들은 시기상조였을지도 모릅니다. 80년대부터 C를 사용하지 않았다는 점을 고려하면, 서로 상반되는 문법(방언)이 있을까봐 두려웠습니다. 제가 공유 라이브러리를 쓰기 위해 C를 사용했을 때 컴파일러 판매자들은 지배력을 얻기 위해 싸우고 있었습니다.
Manx (Aztec C), Haage & Partner (Storm C), Watcom 같은 회사들은 각기 다른 표준으로 그 당시 개발자들을 매우 힘들게 만들었습니다.
#ifndef bufferH #define bufferH
#include <memory> #include <vcl.h>
class TQTXBuffer { private: UInt8* FBuffer = NULL; int FSize = 0;
protected: bool getEmpty(); int getSize(); UInt8 getItem(int Index); void setItem(int Index, UInt8 Value);
public: __property bool Empty = { read = getEmpty }; __property int Size = { read = getSize }; __property UInt8 Items[int Index] = { read = getItem, write = setItem };
bool allocate(int BytesToAllocate); bool release();
int read(int Index, int BytesToRead, UInt8* Target); int write(int Index, int BytesToWrite, UInt8* Source);
void zero(); void fillAll(UInt8 Value); void fill(int Index, int BytesToFill, UInt8 Value);
~TQTXBuffer(); }; #endif |
구현
구현 부분을 살펴 보겠습니다 !
#pragma hdrstop #include "buffer.h" #include <memory> #include <vcl.h> #pragma package(smart_init)
const char* cnt_err_no_memory = "No memory allocated error"; const char* cnt_err_index_invalid = "Invalid index error";
TQTXBuffer::~TQTXBuffer() { if (FBuffer != NULL) release(); }
void TQTXBuffer::zero() { if (!getEmpty()) { memset(FBuffer, 0, FSize); } }
void TQTXBuffer::fillAll(UInt8 Value) { if ( !getEmpty() ) { memset(FBuffer, Value, FSize); } }
void TQTXBuffer::fill(int Index, int BytesToFill, UInt8 Value) { if (!getEmpty()) {
if ( (Index >= 0) && (Index < FSize) ) { if (BytesToFill > 0) BytesToFill -= (FSize - Index);
if (BytesToFill < 1) return;
UInt8* lRef = FBuffer; lRef += Index;
memset(lRef, Value, BytesToFill); } else throw new Exception(cnt_err_index_invalid); } else throw new Exception(cnt_err_no_memory); }
bool TQTXBuffer::allocate(int BytesToAllocate) { if (!getEmpty()) release();
try { FBuffer = (UInt8*) malloc(BytesToAllocate); FSize = BytesToAllocate; } catch (...) { FBuffer = NULL; FSize = 0; return false; } memset(FBuffer, 0, FSize); return true; }
bool TQTXBuffer::release() { if (!getEmpty()) { free(FBuffer); FBuffer = NULL; FSize = 0; return true; } else return false; }
int TQTXBuffer::getSize() { return FSize; }
bool TQTXBuffer::getEmpty() { return (FBuffer == NULL); }
UInt8 TQTXBuffer::getItem(int Index) { if ( !getEmpty() ) { if ( (Index >= 0) && (Index < FSize) ) { UInt8* lRef = FBuffer; lRef += Index; return *lRef; } else throw new Exception(cnt_err_index_invalid); } else throw new Exception(cnt_err_no_memory); }
void TQTXBuffer::setItem(int Index, UInt8 Value) { if ( !getEmpty() ) { if ( (Index >= 0) && (Index < FSize) ) { UInt8* lRef = FBuffer; lRef += Index; *lRef = Value; } else throw new Exception(cnt_err_index_invalid); } else throw new Exception(cnt_err_no_memory); }
int TQTXBuffer::read(int Index, int BytesToRead, UInt8* Target) { if ( !getEmpty() ) { if ( (Index >= 0) && (Index < FSize) ) { if (BytesToRead > 0) BytesToRead -= (FSize - Index);
if (BytesToRead < 1) return 0;
UInt8* lRef = FBuffer; lRef += Index;
memcpy(Target, lRef, BytesToRead);
return BytesToRead; } else throw new Exception(cnt_err_index_invalid); } else throw new Exception(cnt_err_no_memory); }
int TQTXBuffer::write(int Index, int BytesToWrite, UInt8* Source) { if ( !getEmpty() ) { if ( (Index >= 0) && (Index < FSize) ) { if (Index + BytesToWrite > FSize) BytesToWrite -= (FSize - Index);
if (BytesToWrite < 1) return 0;
UInt8* lRef = FBuffer; lRef += Index;
memcpy(lRef, Source, BytesToWrite);
return BytesToWrite; } else throw new Exception(cnt_err_index_invalid); } else throw new Exception(cnt_err_no_memory); } |
위의 내용은 6 시간 동안 언어를 살펴보면서 작업한 것입니다. 자신이 알고 있는것부터 시작하여 자신의 속도로 지식을 넓힐 수 있습니다.
클래스는 무엇을 합니까?
델파이 개발자는 클래스의 private, protected 및 public 섹션을 인식하게됩니다. 델파이에 익숙한 경우 속성은 새로운 방식으로 정의됩니다. 여기서 getter 및 setter 메소드는 {} 코드 블록 내에 지정됩니다 (이러한 이유가 있습니다).
그 외 구문들은 매우 친숙할 것입니다. 오브젝트 파스칼 개발자들에게 유일하게 생소한 단어는 아마도 "void"일 것입니다. 이는 단순히 "nothing"을 의미합니다. C 언어에서 모든 메소드는 함수이며 데이터 유형은 메소드 이름 앞에 정의됩니다. Void는 단순히 컴파일러에게이 함수가 데이터를 반환하지 않는다고 알려주므로 오브젝트 파스칼에서는 프로 시저라고합니다.
델파이 개발자들은 클래스 메소드를 다음과 같이 정의하는 데 익숙합니다.
Procedure TSomeClass.MethodName; begin end; |
그러나 C ++에서는 다음과 같습니다.
void TSomeClass::MethodName { } |
클래스는 책들에서 하나의 장으로 다룰 정도로 큰 주제이지만 여기서는 클래스의 일부분만 다룹니다. 이 글에서 보여 준 것처럼 간단한 것들을 위해 도서들이 필요하지는 않지만, 참고용으로 책들을 곁에 두고 참조하는 것을 좋아합니다. 저는 자기 전에 읽기를 좋아하는 사람 중 한 명이고, 더 배우고 싶은 것을 만날 때마다 잠자기 전에 읽어서 잠재의식이 잠자는 동안 정보를 소화할 수 있도록 합니다.
그럼 내가 방금 작성한 클래스는 어떻게 사용합니까 ? 간단합니다. 그것을 바이트의 큰 배열이라고 생각하십시오. 클래스 자체는 더 많은 작업을 제공할 경우 유용할 수 있습니다(TP 상존에서 상속하는 것은 논리적인 다음 단계일 것이며 인스턴스 간에 데이터를 이동할 수 있도록 Assign()을 구현하는 것입니다). 스트림을 저장하고 스트림에 로드하는 것은 마찬가지로 더 유용하게 만들 것이며, 아마도 개별 비트를 설정하고 가져오는 기능을 추가할 것입니다.)
사용 방법에 대한 간단한 예는 다음과 같습니다.
// create a new instance TQTXBuffer *buff = new TQTXBuffer();
// allocate a 50 byte buffer buff->allocate(50);
// manually set each byte int x = 0; while (x < buff->Size) { buff->Items[x] = x; x++; }
// Release memory and free instance buff->release(); delete(buff); |
괜찮은 제 2의 언어를 찾고 있다면 C ++ 빌더를 시도 해보시길 권합니디. 시간과 실패를 많이 줄여줍니다. 구글은 대단하지만, 예전 방식으로 배우는 것이 가장 좋습니다.
이것은 새로운 언어를 사용한 6 시간의 결과물로서, 동일한 결과를 달성하는데 더 나은 방법이 많이 있을 것이라는 점을 명심하십시오.
리플렉션
이 글은 결코 튜토리얼이 아니며 이미 델파이를 사용하고 마스터하면 C ++을 배우는 것이 얼마나 쉬운지를 보여주는데 중점을 두고 있습니다. 개인적으로 델파이와 번들로 묶을 수 있는 더 쉬운 언어들을 떠올려보았습니다 - 하지만 이 두 언어가 멋진 (교향곡 협연에 필요한것처럼)개발을 완성하기 위해 사용되는 데에는 다 이유가 있습니다. 엠바카데로가 집중하는 개발 사이클을 이 두 언어는 대표적으로 보여주고 있습니다.
처음에는 조금 어려웠으나 접근 방식을 터득하면서 정말로 그 경험을 크게 즐기기 시작했습니다! 왜 이제서야 C ++ 빌더에게 관심을주고 사용하기 시작했는지 ! 스스로에게 물어보고 싶을 정도로 말입니다.
C++빌더를 사용하는 것은 비주얼 스튜디오에서의 나의 경험과 극명한 대조를 이룹니다. VCL은 정말 높은 가치를 가지고 있습니다. VCL이나 FMX 없이 C/C++로 WinAPI용 소프트웨어를 작성하는 것은 매우 어려울 것입니다.
하지만 C++빌더를 통해 델파이 개발자로서의 기술을 재응용할 수 있었고 몇 시간만 노력해도 생상성을 높일 수 있었습니다.
C++에 관해서는 초보자이지만, 델파이에서 얻은 대부분의 지식은 직접 이전할 수 있으므로 이 두 언어를 훨씬 쉽게 연결할 수 있습니다.
델파이와 C++ 빌더의 힘을 결합하여 큰 효과를 거둘 수있는 방법을 살펴보면 이 주제에 공감하실 것입니다.
지금까지의 경험은 완전히 즐거웠습니다! 모든 델파이 개발자에게 강력히 추천합니다 !