11 3월 2024
번역기가 C# 언어에서 C++로 구문 구조를 변환하는 방법에 대해 논의해 보겠습니다. 이 과정에서 발생하는 번역의 세부 사항과 제한 사항을 살펴보겠습니다.
번역은 프로젝트별로 이루어집니다. 하나의 C# 프로젝트는 하나 또는 두 개의 C++ 프로젝트로 변환됩니다. 첫 번째 프로젝트는 C# 프로젝트를 반영하며, 두 번째 프로젝트는 원본 프로젝트에 테스트가 존재하는 경우 테스트를 실행하기 위한 googletest 애플리케이션으로 사용됩니다. 각 입력 프로젝트에 대해 CMakeLists.txt 파일이 생성되어 대부분의 빌드 시스템에 대한 프로젝트 생성을 허용합니다.
보통 하나의 .cs 파일은 하나의 .h 파일과 하나의 .cpp 파일에 해당합니다. 일반적으로 타입 정의는 헤더 파일에 들어가고, 메소드 정의는 소스 코드 파일에 위치합니다. 그러나 템플릿 타입의 경우 모든 코드가 헤더 파일에 남아 있습니다. 적어도 하나의 공개 정의를 포함하는 헤더 파일은 include 디렉토리에 들어가 의존 프로젝트와 최종 사용자가 접근할 수 있습니다. 내부 정의만 있는 헤더 파일은 source 디렉토리로 갑니다.
원본 C# 코드의 번역에서 얻은 코드 파일 외에도, 번역기는 서비스 코드를 포함하는 추가 파일을 생성합니다. 이 프로젝트의 타입을 헤더 파일에서 어디에서 찾을 수 있는지를 지정하는 항목이 포함된 구성 파일도 출력 디렉토리에 배치됩니다. 이 정보는 의존 어셈블리를 처리하는 데 필요합니다. 또한, 포괄적인 번역 로그가 출력 디렉토리에 저장됩니다.
using <typename> = ...
구문을 사용하여 변환됩니다.enum class
구문 사용).System::MulticastDelegate
클래스의 특수화를 위한 별칭으로 변환됩니다.public delegate int IntIntDlg(int n);
using IntIntDlg = System::MulticastDelegate<int32_t(int32_t)>;
System.Object
의 암시적 상속은 명시적 상속이 됩니다.이러한 모든 요인으로 인해 몇 가지 제한 사항이 적용됩니다:
C# 동작을 엄격하게 모방하려면 다소 다른 접근 방식이 필요하다는 것을 알고 있습니다. 그럼에도 불구하고 이 로직을 선택한 이유는 변환된 라이브러리의 API를 C++ 패러다임에 더 가깝게 일치시키기 때문입니다. 아래 예시는 이러한 기능을 설명합니다:
C# 코드:
using System;
public class Base
{
public virtual void Foo1()
{ }
public void Bar()
{ }
}
public interface IFoo
{
void Foo1();
void Foo2();
void Foo3();
}
public interface IBar
{
void Bar();
}
public class Child : Base, IFoo, IBar
{
public void Foo2()
{ }
public virtual void Foo3()
{ }
public T Bazz<T>(object o) where T : class
{
if (o is T)
return (T)o;
else
return default(T);
}
}
C++ 헤더 파일:
#pragma once
#include <system/object_ext.h>
#include <system/exceptions.h>
#include <system/default.h>
#include <system/constraints.h>
class Base : public virtual System::Object
{
typedef Base ThisType;
typedef System::Object BaseType;
typedef ::System::BaseTypesInfo<BaseType> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
virtual void Foo1();
void Bar();
};
class IFoo : public virtual System::Object
{
typedef IFoo ThisType;
typedef System::Object BaseType;
typedef ::System::BaseTypesInfo<BaseType> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
virtual void Foo1() = 0;
virtual void Foo2() = 0;
virtual void Foo3() = 0;
};
class IBar : public virtual System::Object
{
typedef IBar ThisType;
typedef System::Object BaseType;
typedef ::System::BaseTypesInfo<BaseType> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
virtual void Bar() = 0;
};
class Child : public Base, public IFoo, public IBar
{
typedef Child ThisType;
typedef Base BaseType;
typedef IFoo BaseType1;
typedef IBar BaseType2;
typedef ::System::BaseTypesInfo<BaseType, BaseType1, BaseType2> ThisTypeBaseTypesInfo;
RTTI_INFO_DECL();
public:
void Foo1() override;
void Bar() override;
void Foo2() override;
void Foo3() override;
template <typename T>
T Bazz(System::SharedPtr<System::Object> o)
{
assert_is_cs_class(T);
if (System::ObjectExt::Is<T>(o))
{
return System::StaticCast<typename T::Pointee_>(o);
}
else
{
return System::Default<T>();
}
}
};
C++ 소스 코드:
#include "Class1.h"
RTTI_INFO_IMPL_HASH(788057553u, ::Base, ThisTypeBaseTypesInfo);
void Base::Foo1()
{
}
void Base::Bar()
{
}
RTTI_INFO_IMPL_HASH(1733877629u, ::IFoo, ThisTypeBaseTypesInfo);
RTTI_INFO_IMPL_HASH(1699913226u, ::IBar, ThisTypeBaseTypesInfo);
RTTI_INFO_IMPL_HASH(3787596220u, ::Child, ThisTypeBaseTypesInfo);
void Child::Foo1()
{
Base::Foo1();
}
void Child::Bar()
{
Base::Bar();
}
void Child::Foo2()
{
}
void Child::Foo3()
{
}
변환된 각 클래스의 시작 부분에 있는 일련의 별칭과 매크로는 특정 C# 메커니즘을 에뮬레이션하는 데 사용되며, 주로 GetType
, typeof
, is
가 사용됩니다. .cpp 파일의 해시 코드는 효율적인 유형 비교를 위해 사용됩니다. 인터페이스를 구현하는 모든 함수는 C# 동작과는 다르지만 가상입니다.