26 1월 2024

C#에서 C++로: 프로젝트 변환을 자동화한 방법 - 2부

개발

C#에서 C++ 코드 번역기의 설계 및 개발은 CodePorting에서 독립적으로 수행되었습니다. 이 작업은 여러 가지 조사, 다양한 접근 방식 적용 및 메모리 모델 및 기타 측면에서 다양한 테스트를 필요로 했습니다. 결국 두 가지 솔루션을 선택했습니다. 그 중 하나는 현재 Aspose 제품의 C++ 릴리스에 사용되고 있습니다.

기술

이제 코드 번역기에서 사용하는 기술을 설명할 차례입니다. 번역기는 C#으로 작성된 콘솔 응용 프로그램으로, 일반적인 시퀀스(예: 번역-컴파일-테스트)를 수행하는 스크립트에 쉽게 통합됩니다. 또한 버튼을 클릭하여 동일한 작업을 수행할 수 있는 GUI 구성 요소도 있습니다.

구문 분석은 구식 버전의 번역기에서 NRefactory 라이브러리를 사용하며 새 버전에서는 Roslyn을 사용합니다.

번역기는 정보 수집 및 C++ 코드 출력을 위해 여러 AST 트리 탐색을 수행합니다. C++ 코드에는 AST 표현이 생성되지 않으며 대신 순수 텍스트 형식으로 출력 코드를 처리합니다.

번역기를 미세 조정하기 위해 추가 정보가 필요한 경우가 많습니다. 이 정보는 옵션과 속성을 통해 전달됩니다. 옵션은 전체 프로젝트에 적용되며 일반적으로 코드를 구문 분석할 때 사용되는 클래스 내보내기 매크로 이름이나 C# 조건 기호를 지정하는 데 사용됩니다. 속성은 유형 및 엔터티에 적용되며 특정 정보를 제공합니다. 예를 들어 번역된 코드에서 어떤 클래스 멤버가 const 또는 mutable 한정자를 필요로 하는지 또는 어떤 엔터티를 번역에서 제외해야 하는지를 표시합니다.

C# 클래스와 구조는 C++ 클래스로 변환됩니다. 멤버 및 소스 코드는 가장 유사한 형태로 변환됩니다. 제네릭 유형 및 메서드는 C++ 템플릿으로 매핑됩니다. C# 참조는 스마트 포인터(공유 또는 약한 참조)로 번역됩니다. 참조 클래스는 라이브러리에서 정의됩니다. 코드 번역기의 기타 내부 세부 정보는 별도의 기사에서 설명됩니다.

따라서 C#에서 C++로 번역된 프로젝트는 .NET 라이브러리 대신 저희 라이브러리에 의존합니다:

C# to C++

C#에서 C++로 번역기 Library와 번역된 프로젝트를 구축하기 위해 Cmake을 사용합니다. 현재는 VS 2017 및 2019(Windows), GCC 및 Clang(Linux) 컴파일러를 지원하고 있습니다.

이미 언급했듯이, 대부분의 .NET 구현은 다음과 같은 타사 라이브러리 위에 얇은 어댑터로 구성됩니다:

  • Skia — 그래픽 지원.
  • Botan — 암호화 기능.
  • ICU — 문자열, 코드 페이지 및 문화 지원.
  • Libxml2 — XML 작업.
  • PCRE2 — 정규 표현식 지원.
  • zlib — 압축 기능.
  • Boost — 다양한 용도.
  • 기타 몇 가지 라이브러리.

번역기와 라이브러리는 많은 테스트로 검증되었습니다. 라이브러리 테스트는 GoogleTest 프레임워크를 사용하고, 번역기 테스트는 주로 NUnit/xUnit로 작성되며 다음과 같은 여러 범주로 나뉩니다:

  • 번역기의 출력이 특정 입력 데이터에서 목표와 일치합니다.
  • 번역된 프로그램의 출력이 목표와 일치합니다.
  • 입력 프로젝트의 NUnit/xUnit 테스트가 GoogleTest로 번역되고 통과합니다.
  • 번역된 프로젝트의 API가 C++에서 잘 작동합니다.
  • 번역기 옵션과 속성이 예상대로 작동합니다.

버전 관리 시스템으로 GitLab을 사용하고, CI에는 Jenkins를 사용합니다. 번역된 제품은 NuGet 패키지 및 다운로드 가능한 아카이브로 제공됩니다.

문제점

이 프로젝트를 진행하면서 다양한 문제에 직면했습니다. 일부는 예상했던 것이고, 다른 일부는 그 과정에서 발견되었습니다:

  1. .NET과 C++ 간의 유형 시스템 차이점.
    C++은 Object 유형에 대한 대체가 없으며 대부분의 라이브러리 클래스에 RTTI가 없습니다. 이로 인해 .NET 유형을 STL 유형으로 매핑하는 것이 불가능합니다.
  2. 번역 알고리즘은 복잡합니다.
    번역된 코드에서 많은 세밀한 뉘앙스를 해결해야 합니다. 예를 들어 C#은 메서드 인수를 계산하는 정의된 순서가 있지만, C++에서는 UB가 발생합니다.
  3. 문제 해결이 어렵습니다.
    번역된 코드를 디버깅하려면 특정 기술이 필요합니다. 위에서 설명한 뉘앙스는 프로그램의 작동에 중대한 영향을 미칠 수 있으며 설명하기 어려운 오류를 생성할 수 있습니다. 반면, 이러한 오류는 쉽게 숨겨진 버그로 변할 수 있고 오랫동안 남아 있을 수 있습니다.
  4. 메모리 관리 시스템이 다릅니다.
    C++은 가비지 컬렉션이 없습니다. 따라서 번역된 코드가 원래 코드와 같이 작동하도록 하려면 더 많은 리소스가 필요합니다.
  5. C# 개발자에게는 규율이 필요합니다.
    C# 개발자는 코드 번역 프로세스로 인한 제한 사항에 익숙해져야 합니다. 이러한 제한 사항의 이유:
    • 언어 버전은 번역기 구문 분석기에서 지원되어야 합니다.
    • 번역기에서 지원되지 않는 코드 구조는 금지됩니다(예: yield).
    • 코드 스타일은 번역된 코드 구조로 제한됩니다(예: 각 참조 필드는 약한 참조 또는 공유 참조 중 하나여야 하며 임의의 C# 코드에서는 반드시 그렇지 않습니다).
    • C++ 언어는 자체 제한 사항을 부과합니다(예: C#에서 정적 변수는 모든 전경 스레드가 완료되기 전에 삭제되지 않지만 C++에서는 그렇지 않습니다).
  6. 많은 작업량.
    제품에서 사용하는 .NET 라이브러리의 하위 집합은 충분히 크며 모든 클래스와 메서드를 구현하는 데 많은 시간이 걸립니다.
  7. 개발자에게 특별한 요구 사항.
    복잡한 플랫폼 내부를 깊게 파고들고 두 개 이상의 프로그래밍 언어를 사용해야 하는 필요로 인해 사용 가능한 후보자 수가 제한됩니다. 반면, 컴파일러 이론이나 기타 독특한 분야에 관심 있는 개발자는 프로젝트에서 쉽게 자리를 잡을 수 있습니다.
  8. 시스템의 취약성.
    번역기를 테스트하기 위해 수천 개의 테스트와 수백만 줄의 코드를 사용하고 있지만 때로는 하나의 프로젝트의 컴파일 오류를 수정하기 위해 수행한 변경 사항이 다른 프로젝트에서 오류를 발생시킬 수 있습니다. 예를 들어 특정 구문과 특정 코드 스타일이 드물게 발생하는 경우가 있습니다.
  9. 진입 장벽이 높습니다.
    코드 번역기 프로젝트의 대부분의 작업은 깊은 분석을 필요로 합니다. 다양한 하위 시스템과 시나리오 때문에 각 새로운 작업은 프로젝트의 새로운 측면에 대해 오랜 시간 동안 익숙해지는 것이 필요합니다.
  10. 지적 재산권 보호 문제.
    C# 코드를 효과적으로 난독화하는 다양한 솔루션이 있지만, C++에서는 클래스 헤더에 많은 정보가 보존됩니다. 또한 일부 정의는 후속적인 결과 없이 공개 헤더에서 제거할 수 없습니다. 제네릭 클래스와 메서드를 템플릿으로 매핑하는 것은 알고리즘을 노출시키는 취약점을 만듭니다.

그럼에도 불구하고 코드 번역기 프로젝트는 기술적인 관점에서 매우 흥미로우며, 학문적인 복잡성으로 인해 우리는 항상 새로운 것을 배우게 됩니다.

결론

코드 번역기 프로젝트를 진행하면서 우리는 코드 번역이라는 흥미로운 학문적인 과제를 해결하는 시스템을 성공적으로 구현했습니다. 우리는 Aspose 라이브러리의 월간 릴리스를 해당 언어로 작동하지 않아야 할 언어로 조직했습니다.

코드 번역기에 관한 더 많은 기사를 게시할 계획입니다. 다음 기사에서는 구체적인 C# 구조가 C++ 구조로 어떻게 매핑되는지를 포함하여 변환 프로세스를 자세히 설명합니다. 다른 기사에서는 메모리 관리 모델에 대해 이야기할 것입니다.

질문에 답하기 위해 최선을 다하겠습니다. 독자들이 코드 번역기 개발의 다른 측면에 관심이 있다면 더 많은 기사를 작성하는 것도 고려해 볼 수 있습니다.

관련 뉴스

관련 동영상

관련 기사