요약
이 상세한 기사는 역사적으로 성장한 코드베이스의 주요 부분을 현대 C++로 변환하기 위한 슬롯 커뮤니티의 장기적인 노력을 다루고 있습니다. 처음에는 Pascal에서 개발되었고 나중에는 Delphi(학술적 인기로 인해)에서 개발된 슬롯 커뮤니티(일반 대수 모델링 시스템)는 Delphi 컴파일러의 성능 및 이식성 제한을 해결하기 위해 사내 Pascal-to-C 트랜스파일러(p3c)로 전환되었습니다. p3c 유지 관리가 정체됨에 따라 슬롯 커뮤니티는 향상된 도구, 더 큰 프로그래머 풀 및 향상된 성능을 활용하기 위해 C++17로 마이그레이션했습니다. 이 문서에서는 Delphi에서 C++17로의 슬롯 커뮤니티 여정에 대해 설명합니다.
이 변화로 인해 C++의 별도 컴파일 모델, 언어 기능의 차이(예: 중첩 함수, 배열 인덱싱, 레코드의 변형 부분, "with" 문)로 인해 컴파일 시간이 길어지고 표준 라이브러리의 모든 범용 C++ STL 컨테이너에 대해 맞춤형 손으로 쓴 데이터 구조를 유지해야 하는 필요성과 같은 문제가 발생합니다.
CMEX(CoMpilation 및 EXecution 시스템 - "슬롯 커뮤니티" 호출 뒤에 있는 프로그램) 및 GDX 유틸리티와 같은 주요 프로그램이 번역되고 있습니다. GDX 라이브러리 및 유틸리티는 이미 C++17로 완전히 번역되었습니다.GitHub에서 사용 가능오픈 소스 소프트웨어로. CMEX C++17 번역의 컴파일러 부분은 Delphi 버전과 기능이 동일하지만 실행 시스템은 아직 진행 중인 작업입니다. C++ 컴파일러 미리보기는 다음을 통해 활성화할 수 있습니다.컴파일러 미리보기용감한 사람들을 위한 슬롯 커뮤니티 옵션.
역사적 배경
일반 대수 모델링 시스템은 1970년대 초 세계 은행에서 근무하는 팀에 의해 처음 고안되었습니다. 진행 중인 작업 시스템에 대한 첫 번째 주목할만한 프레젠테이션은 다음과 같습니다.1976년 부다페스트에서 수학 프로그래밍에 관한 국제 심포지엄. 갑나중에 출판1982년 Johannes Bisschop 및 Alexander Meeraus 제목"전략 계획 환경에서 일반 대수 모델링 시스템 개발에 대하여"이미 표 형식의 데이터 정의 및 집합, 매개변수, 방정식 및 변수가 포함된 대수 표기법에서 영감을 받은 구문과 같은 슬롯 커뮤니티 언어의 핵심 개념을 보여줍니다. 슬롯 커뮤니티 모델링 언어의 첫 번째 구현은 세계 은행에서 Fortran으로 작성되었습니다. 1970년대 후반과 1980년대 초반에 Niklaus Wirth의 교육용 언어인 Pascal이 학계에서 높은 인기를 얻었고 점차 채택 측면에서 Fortran을 추월하기 시작했습니다. 이는 슬롯 커뮤니티의 주요 구현이 Pascal로 마이그레이션된 이유와 슬롯 커뮤니티 자체 및 지원 유틸리티 프로그램에 대한 대부분의 코드가 Pascal 또는 그 이후의 객체 지향 후속 버전인 Object Pascal이라고도 알려진 Delphi에서 공식화되었는지 설명합니다. 슬롯 커뮤니티의 역사에 관한 추가 정보는 다음에서 확인할 수 있습니다.이 블로그 게시물슬롯 커뮤니티의 중요한 기여자이기도 한 David Kendrick의 사망을 기념하는 행사입니다.
중간 단계로서의 p3c 트랜스파일러
Borland와 그 구매자 회사의 독점적이고 무료인 Pascal 및 Delphi 컴파일러는 수년 동안(결국 Embarcadero에서 종료될 때까지) 생성된 기계어 코드의 성능과 다양한 플랫폼에 대한 이식성이 GCC 및 이후 Clang과 같은 최신 C 컴파일러에 비해 부족했기 때문에 덴마크 기술 대학의 Søren S. Nielsen은 p3c라는 파스칼에서 C로의 변환 유틸리티를 개발했습니다(이는Dave Gillespie의 GNU 프로젝트 p2c 트랜스파일러). 이를 통해 슬롯 커뮤니티는 역사적으로 성장한 Pascal 코드베이스를 계속 유지하면서도 C 도구의 발전으로부터 이점을 얻을 수 있었지만, 트랜스파일러는 사람이 사용할 수 있는 코드를 생성하기 위한 것이 아니라 C 컴파일러가 신속하게 효율적인 기계어 코드로 변환할 수 있는 C 코드를 생성하는 데 중점을 두었습니다. 변환 도구 자체에 대한 작업과 이를 최근에 사용 가능한 객체 지향 Delphi 언어 기능으로 확장하는 것 외에도 Søren은 Pascal 및 Delphi 표준 라이브러리 기능의 부분적 재구현도 작성했습니다. 일부 호출은 유사한 함수 이름을 사용하여 C 표준 라이브러리 함수 호출로 직접 변환될 수 있지만 반올림과 같은 일부 기본 함수는 C 및 Pascal에서 근본적으로 다르게 동작합니다. 컴파일러와 기본 라이브러리(수학과 같은)도 예를 들어 로그를 계산할 때 미묘한 차이를 초래할 수 있습니다. 예를 들어 Microsoft 및 Intel C 표준 라이브러리의 로그 함수는 0.15에 대해 인수로 약간 다른 결과를 반환합니다.
트랜스파일러는 2000년대 델파이 버전까지 파스칼 및 델파이 언어 표준의 큰 하위 집합을 지원하므로 매우 인상적입니다. 또한 런타임 성능이 좋고, 유용한 오류 메시지가 있으며, 어느 정도 읽기 쉬우면서도 효율적인 기계어 코드로 컴파일할 수 있는 C 코드를 생성합니다.
안타깝게도 트랜스파일러의 원저자이자 주요 기여자인 Søren S. Nielsen이 예기치 않게 젊은 나이에 세상을 떠났습니다. p3c 외에도 Søren은 재능 있는 연구원이었으며 " 도서관의 동반 도서를 공동 집필했습니다.실용적인 재무 최적화 모델” 이것은슬롯 커뮤니티에 포함됨. 그의 공동 저자그를 인정해 주세요많은 "finlib" 모델의 슬롯 커뮤니티 구현을 위한 것입니다. 그의 손실은 슬롯 커뮤니티에 깊은 영향을 미쳤고 그 결과 p3c 개발은 그 후 몇 년 동안 느려졌지만 베테랑 슬롯 커뮤니티 개발자이자 현 회장인 Steven Dirkse가 예외를 스레드로부터 안전하게 만들기 위해 p3c 내부를 대대적으로 재작업한 후(대상 언어로 C에서 C++로 전환 포함) 또 다른 활동이 시작되었습니다. 제한된 개발자 요구 사항, 레거시 Delphi 코드(예: 슬롯 커뮤니티 IDE)에 대한 강력한 지원에 의존하고 p3c를 간결하게 유지하려는 의도가 결합되어 제네릭, 클로저 및 foreach 루프와 같은 Delphi 7 이후 언어 기능의 도입은 결코 실현되지 않았습니다.
덜 원활한 디버깅 경험은 C 컴파일러가 최종적으로 실행 가능한 기계어 코드를 생성하기 전에 먼저 소스 Delphi를 C로 트랜스파일하는 2단계 빌드 접근 방식의 또 다른 단점입니다. 대부분의 문제는 Lazarus IDE와 같은 대화형 Delphi 디버거를 사용하여 직접 디버깅할 수 있지만 멀티스레딩 및 소켓 통신과 같은 일부 최신 기능은 원시 Delphi에서 완전히 구현되지 않았으며 트랜스파일러를 사용할 때 인라인 C 및 C++ 코드를 통해서만 지원됩니다. 이러한 기능을 사용하려면 gdb 또는 lldb와 같은 디버거에서 중간 C 코드를 디버깅해야 합니다.
최신 C++로의 수동 번역을 통해 코드베이스 미래 보장
왜 C++17인가요?
Delphi는 2000년대 초반 이후 인기를 잃었고 따라서 Delphi 경험이 풍부한 프로그래머를 쉽게 고용할 수 없게 되었고 트랜스파일러에 대한 관심도 점점 줄어들면서 슬롯 커뮤니티는 전체 코드베이스를 Delphi에서 C++로 마이그레이션해야 한다는 압력을 가중시켰습니다. C++는 다른 최신 언어에 비해 제한된 성능 저하로 현대적인 추상화를 제공하는 인기 있는 시스템 프로그래밍 언어이기 때문에 선택되었습니다. 슬롯 커뮤니티에는 이미 C로 작성된 라이브러리가 있고 이전에 Delphi로 작성된 도구는 C API를 통해 동적 라이브러리와 통신하는 경우가 많기 때문에 C와의 호환성도 매우 유익한 것 같습니다. 언어 버전 C++17은 모든 주요 컴파일러(MSVC, GCC, Clang, AppleClang 및 Intel의 C++ 컴파일러)에서 완벽하게 지원되므로 합리적으로 보입니다. 원시 C와 비교하여 C++는 객체 지향, 람다, 템플릿 및 보다 광범위한 표준 라이브러리와 같은 유지 관리성을 향상시키는 추가 추상화를 제공합니다.
Delphi와 비교하여 C++는 사용 가능한 도구 측면에서도 한 단계 더 발전했습니다. C++를 위한 좋은 통합 개발 환경(Visual Studio, VSCode, CLion, QtCreator 등의 좋은 예)과 많은 정적 분석기, 컴파일러, 디버거 및 프로파일러가 많이 있습니다. C++ 컴파일러는 다양한 플랫폼과 운영 체제를 대상으로 할 수 있습니다. 또한 C++에 사용할 수 있는 타사 라이브러리가 더 많고 언어 자체도 최근 몇 년 동안 Delphi보다 더 빠르게 발전했습니다.
새로운 대상 언어에 대한 잠재적인 대안 선택은 Rust, Nim, Zig 또는 Go일 수 있지만 원활한 C 상호 운용성(Rust), 원시 성능(Go), 도구 가용성(Nim, Zig) 측면에서 부족하거나 꽤 어리기 때문에 향후 20~30년 동안 여전히 관련성이 있을 가능성이 부족합니다(참조린디 효과). 따라서 C++를 사용하는 것은 자체(C++98) 및 C 루트와의 하위 호환성으로 인한 단점에도 불구하고 안전한 선택처럼 보입니다.
컴파일 속도 회귀
C++에 대해 하위 호환성 문제가 있음: 소프트웨어를 파스칼 파생 언어에서 C 파생 언어로 번역할 때 말하는 주요 "다운그레이드" 중 하나는 완전한 재구축에 필요한 시간입니다. 컴파일 단위에 인터페이스(예: 함수 서명)와 구현(예: 실제 함수 코드)이 함께 포함되는 Delphi와 달리 C++ 프로그래밍 언어는 역사적으로 등장한 C와 별도의 컴파일 모델을 공유합니다. 전처리기 기반 포함 지시문을 사용하여 인터페이스와 구현을 다른 파일로 분리하면 자체 포함된 단위와 보다 스마트한 "사용" 종속성 추적을 갖춘 Pascal/Delphi 컴파일 모델에 비해 빌드 시간이 훨씬 길어집니다. C 및 C++ 컴파일러는 프로그램을 빌드할 때 헤더 파일을 여러 번 처리해야 하는 경우가 많습니다. 매크로 및 정의와 같은 전처리기 작업이 포함 순서에 따라 정확한 내용에 영향을 미칠 수 있기 때문입니다. C 및 C++의 긴 빌드 시간은 가능한 경우 전방 선언을 사용하여 불필요한 포함을 줄이면 약간 개선될 수 있습니다(예: 다음과 같은 도구의 도움으로)이유), 사전 컴파일된 헤더와 같은 캐싱 메커니즘을 사용하고ccache(또는sccache). 처음 두 개는 추가 수동 작업이 필요한 반면, 프로젝트의 여러 부분에서 다를 수 있는 전처리기 정의 설정에 의존하는 헤더 파일의 코드와 수정된 컴파일러 인수로 인해 캐시 누락으로 인해 캐싱의 효율성이 제한됩니다. C++20은 모듈을 솔루션으로 도입했고 슬롯 커뮤니티 내부 첫 번째 실험은 2021년에 이미 유망했지만, 현재 2025년에 슬롯 커뮤니티 배포판(MSVC, Clang, GCC, Intel C++)을 구축하는 데 사용된 컴파일러는 아직 C++20 모듈 표준의 이식 가능하고 완전한 구현을 제공하지 않으므로 슬롯 커뮤니티는 여전히 별도의 헤더와 컴파일 단위가 있는 다소 오래된 C++17 표준을 사용해야 합니다.
델파이와 C++ 기능 세트는 다릅니다
C와 Pascal에서 따르는 주요 프로그래밍 패러다임과 자손은 일치하지만 두 언어의 디자인은 세부적으로 서로 다른 철학을 따릅니다. 또한 Pascal과 그 후속 모델인 Delphi는 발전 과정에서 C나 C++에 직접 대응할 수 없는 기능을 받았습니다. 예를 들어, C와 달리 Pascal은 사용자 정의 경계, 비연속 인덱스를 사용하여 배열 인덱스를 더욱 유연하게 허용하며 기본적으로 1 기반 인덱싱을 사용합니다(C와 영향을 받는 많은 언어는 주소 오프셋에서 영감을 받아 0부터 계산하기 시작합니다).
Pascal은 C에서 사용할 수 없는 하위 범위 및 집합 유형도 제공하지만 이는 템플릿 유틸리티를 사용하여 (대부분) 모방할 수 있습니다.클래스 제한<T,lb,ub>. 마찬가지로 C++에는 Delphi의 개체 속성이 없지만 getter 및 setter 메서드로 간단히 대체할 수 있습니다. Delphi에서 C++로 전환하면 안전한 메모리 관리를 위해 수동 대신 스마트 포인터를 사용할 수 있습니다.유형.생성그리고유형.파괴Delphi 객체 지향 코드에서 널리 사용되는 호출). 중첩된 함수 및 프로시저 대신 C++17에는 중첩된 함수를 모방할 수 있으면서도 더욱 강력해지는 보다 유연한 람다 식이 있습니다.
다음과 같은 고정 크기 문자열(문자 버퍼)짧은 문자열Delphi에서 자주 사용되는 문자열은 다음과 같은 보다 유연한 가변 길이 문자열에 비해 성능상의 이점이 있습니다.std::stringC++로 작성되었으며 그 위에 구축된 사용자 정의 클래스를 통해 C++로 쉽게 복제될 수 있습니다.std::배열<char, 256>다음으로 변환하는 편리한 방법 포함std::string및 null로 끝나는 문자 버퍼에 대한 C 스타일 포인터.
번역할 때 세심한 주의가 필요한 또 다른 언어 기능은 델파이 레코드의 변형 부분입니다. C++에서는 공용체 필드가 있는 구조체와 공용체의 대체 부분이 활성화된 변수를 저장하여 대략적으로 계산할 수 있습니다. 이 근사치에도 불구하고 구조체의 필드에 액세스하는 구문은 해당 Delphi 코드와 약간 다릅니다.
C/C++와 Pascal/Delphi 사이의 또 다른 사소하지만 매우 중요한 차이점은 변수와 필드의 기본 0 초기화입니다. 대부분의 경우 이들은 완전히 동일하게 동작하지만, 예를 들어 힙에서 클래스 또는 구조체의 객체를 생성할 때 해당 필드는 내장 유형(예:int)은 C++에서 자동으로 0으로 초기화되지 않지만 Delphi에서는 자동으로 초기화됩니다.
맞춤형 데이터 구조가 일반 데이터 구조보다 더 효율적입니다.
C++17로 마이그레이션한 후 주요 성능 관찰은 슬롯 커뮤니티 코드베이스의 손으로 작성한 동적 배열 및 해시맵 데이터 구조를 대체하는 C++ STL 컨테이너가 부적절하다는 것입니다. RB-트리 기반 사용std::지도그리고std::벡터순진한 대응으로 인해 실행 속도가 크게 느려졌습니다. 이는 C++ 표준 라이브러리 컨테이너가 증가(요소 삽입)만 허용하고 축소(삭제)는 허용하지 않는 슬롯 커뮤니티의 손으로 작성한 데이터 구조보다 더 일반적이고 유연하기 때문에 부분적으로 설명될 수 있습니다. 이러한 사용자 정의 컬렉션 클래스를 심층적으로 변환하여 채택으로 인한 초기 성능 격차를 해소했습니다.표준::*컨테이너를 더 간단하게 대체할 수 있습니다.
"with"-문: 해결이 까다로울 수 있음
C++에는 없는 델파이 기능 중에서 "with" 문은 번역 과정에서 자주 골치 아픈 원인이 됩니다. 본질적으로 이는 하나의 객체에 영향을 미치는 많은 명령이 있을 때 반복을 줄이기 위한 것입니다. 메서드 호출 또는 개체 필드와 속성 읽기 및 쓰기. 예를 들어 “o.x(); o.a := 2;” “로 단축할 수 있습니다.o로 시작 x; a := 2; 끝". 짧은 코드 조각에서 이는 중복과 입력 노력을 줄이는 현명한 방법처럼 보입니다. 그러나 이 기능이 거대한 함수에서 중첩된 방식으로 인플레이션적으로 사용되면 특정 코드 줄이 어떤 개체를 참조하는지 결정하기 어려울 수 있습니다. 언어 사양에서는 "바인딩 포함"이 우선순위를 갖는 마지막 개체를 명확하게 정의하지만 사용 중인 필드 또는 메서드가 있는 관련 개체를 식별하기 위해 여전히 코드베이스에서 많은 스크롤이 필요할 수 있습니다. 따라서 코드 뒷부분에서 with 문을 해결하는 보다 실용적인 방법은 Lazarus IDE(FreePascal용 그래픽 IDE) 디버거에서 이를 살펴보는 것입니다. 이 디버거는 참조된 객체를 줄 위에 툴팁으로 표시합니다. 그러나 "with" 문은 문제가 있는 것으로 널리 알려져 있으며 요즘은 최신 Delphi 프로그래머들만 주의해서 사용합니다.
세밀한 메모리 관리
Delphi와 C++의 또 다른 세부적인 차이점은 메모리 정렬, 패킹 및 크기 조정 세부 사항입니다. 한 가지 예는 열거형 유형의 변수의 기본 너비입니다. 이는 결국 구조체의 메모리 레이아웃에 영향을 미치고 의도하지 않은 패딩을 도입하여 메모리 사용량을 증가시킬 수 있습니다. 단순히 열거형을 선언하면 변수 인스턴스가 int의 전체 너비(x64에서 4바이트)를 갖게 됩니다. 해결 방법은 명시적으로 “로 선언하는 것입니다.열거형 이름 : uint8_t …” 단일 바이트에 저장되도록 강제합니다. 이는 다음 범위의 값을 가진 열거에 가능합니다.0..255.
구조체/레코드 및 클래스 객체는 델파이에서는 힙에 저장되어야 하지만 C++에서는 때때로 스택에 배치될 수 있습니다. 이것이 가능하지 않은 경우, 예를 들어 초기화가 늦어져서 다음과 같은 스마트 포인터가 생겼습니다.std::unique_ptr<T>적어도 해체 호출 및 해제를 자동으로 만드는 데 사용할 수 있습니다. 따라서 C++를 사용하면 Delphi에서처럼 힙을 강제로 사용하는 대신 "더 빠른" 스택에 더 많은 데이터를 저장할 수 있습니다.
현황 및 전망
번역되었거나 번역 중인 주요 슬롯 커뮤니티 프로그램은 다음과 같습니다.Com파일러와예ecution 시스템(CMEX)과 라이브러리 및 유틸리티GAMSData eX파일 변경(GDX). 유틸리티로는 가장 유명한 gdxdump(GDX 내용 인쇄), gdxmerge(두 개의 GDX 파일 병합) 및 gdxdiff(두 개의 GDX 파일 비교)가 있습니다. C++17의 슬롯 커뮤니티 컴파일러(내부적으로 CPPMEX라고 함)는 원래 Delphi 구현과 기능(및 성능)이 거의 동일하지만 실행 시스템은 아직 진행 중인 작업이며 아직 24개의 간단한 슬롯 커뮤니티 모델 라이브러리 모델에서만 작동합니다. 그러나 전체 슬롯 커뮤니티 CMEX가 곧 C++로 번역되면서 작업이 빠르게 진행됩니다. 컴파일러의 C++ 번역을 실험하려는 경우 다음을 사용하여 수동으로 활성화하도록 선택할 수 있습니다.컴파일러 미리보기슬롯 커뮤니티 옵션.