2016-09-25 1 views
2

가 설치C++/CLI 정적 객체는

네이티브 C에 대한 ++ lib 디렉토리는 C++/CLI 래퍼의 번역 단위 범위에서 정적 객체를 만드는 동안, 내가 우연히 런타임 예외가 처리되지 않은 원인 auto 키워드를 사용하여 문제가 발생했습니다. C++/CLI 래퍼는 순전히 기능적이므로 ConcurrentDictionary를 사용하여 호출간에 일부 상태를 유지해야했습니다 (Managed < -> 네이티브 대리자 변환 처리).

static ConcurrentDictionary<String^, String^>^ GlobalData1 = gcnew ConcurrentDictionary<String^, String^>(); 

하지만 이것은 컴파일에 실패 : 내가 처음이 (간체) 시도

정적 저장 기간과 변수는 핸들 또는 추적 참조 형

를 들어를 가질 수 없습니다 단순함, 나는 문제를 파악하는 동안 계속해서 형식 공제를 사용하기로 결정했습니다.

static auto GlobalData3 = gcnew ConcurrentDictionary<String^, String^>(); 

그리고 놀랍게도 이것은 컴파일되고 링크되었습니다! 나는 잠시 더 많은 코드를 작성에 가서 그때는 C++/CLI 래퍼를 사용하여 내 C# 테스트 응용 프로그램을 실행하고 처리되지 않은 런타임 예외를받은 :

처리되지 않은 예외 : System.IO.FileLoadException : 파일을로드 할 수 없습니다 또는 어셈블리 'DotNetTestLibWrapper, 버전 = 1.0.6111.33189, 문화 = 중립, PublicKeyToken = null'또는 종속성 중 하나입니다. 유형을 찾거나로드 할 수 없습니다. (HRESULT 예외 : 0x80131522) ---> System.TypeLoadException : 어셈블리에서 ''형식 'DotNetTestLibWrapper, 버전 = 1.0.6111.33189, Culture = neutral, PublicKeyToken = null'필드에 잘못된 형식의 필드가 있습니다.

다른 수정 사항을 작성한 이후로 문제가 무엇인지 즉시 알지 못했습니다. 퓨전 로그를 켜고 Fuslogvw.exe를 사용하여 더 많은 정보를 얻었습니다. 이 문제는 정적 ConcurrentDictionary를 추론 한 유형에 있습니다. 여기에 위치한이 게시물에 설명 https://github.com/calebwherry/DotNetWrapperForNativeCppLibrary

코드 : https://github.com/calebwherry/DotNetWrapperForNativeCppLibrary/blob/master/DotNetTestLibWrapper/DotNetTestLibWrapper.cpp

MSVS 버전 :

마이크로 소프트 비주얼 스튜디오 커뮤니티 2015 버전 14.0

나는이 문제를 보여주기 위해 SSCCE를 만들었습니다. Microsoft .NET Framework 버전 4.6.01038

,210

질문

이 모두 원인 컴파일러 오류 :

static ConcurrentDictionary<String^, String^>^ GlobalData1 = gcnew ConcurrentDictionary<String^, String^>(); 
static auto^ GlobalData2 = gcnew ConcurrentDictionary<String^, String^>(); 

하지만이 + 링크를 컴파일하지만 불법 유형에 대한 처리되지 않은 런타임 예외가 발생합니다

static auto GlobalData3 = gcnew ConcurrentDictionary<String^, String^>(); 

에 가서 무엇을 왜 이런 일이 생길까요? MSVS는 autoauto^ 정의가 동일하다고 생각하는 것으로 보입니다. (IntelliSense는 이렇게 말합니다.)그러나 그들은 하나가 컴파일되고 다른 하나는 컴파일하지 않기 때문에 분명히 아닙니다.

참고 : 원래 컴파일러 문제에 대한 몇 가지를 읽은 후,이 문제에 실제 해결책은 이것이다 :

괜찮
ref struct GlobalData 
{ 
    static ConcurrentDictionary<String^, String^>^ GlobalData4 = gcnew ConcurrentDictionary<String^, String^>(); 
}; 

, 실제로 유형 공제에 무슨 일이 일어나고 있는지 그냥 궁금 해요.

답변

5

Hmya, 컴파일러 버그, 그런 식으로 신고해서는 안됩니다. 의심의 여지가 C++ 11 변경에 의해 유도 된이 선언을 허용하지 않아야하는 추가 검사는 문에서 auto과 함께 작동하지 않습니다.

호출하려고하는 악명 높은 SIOF (정적 초기화 순서 Fiasco)는 .NET 기능이 아닙니다. 컴파일러가 생성하는 이니셜 라이저 (보통 비 관리 코드에만 사용됨)를 사용하면 생성자가 너무 일찍 실행됩니다. 정확히이 잘못되면 디버거 트레이스에서보기가 어렵습니다. 그다지 예쁜 것은 아닙니다. 하지만 가장 근본적인 문제는 C++/CLI의 실행 환경을 설정하기 위해 모듈 초기화 프로그램을 먼저 실행해야한다는 것입니다. 그리고 그 전에 네이티브 C++ 이니셜 라이저. 그것은 아직 일어나지 않았다.

올바른 방법으로 정적 멤버를 초기화해야합니다 (정적 생성자, 일명 .cctor라고도 함). CLR은 클래스 멤버 중 하나를 사용하기 전에 자동으로 호출합니다. 해당 생성자를 명시 적으로 작성할 필요는 없으며 컴파일러는 필드 초기화 표현식에서 자동으로 작성합니다.

ref class Globals { 
public: 
    static ConcurrentDictionary<String^, String^>^ Data1 = gcnew ConcurrentDictionary<String^, String^>(); 
    // etc... 
}; 
+0

컴파일러가 모듈 초기화 프로그램에서 정적 생성자를 호출하는 컴파일러 생성 클래스 * 및 *를 정의하여 관리되는 "정적"저장소를 지원할 수 있는지 궁금합니다. –

+0

글쎄, 그것은 단지 CLR이 정적 생성자를 지원할 필요가 없다. 그리고 그들은 그렇지 않았습니다. –