2009년 10월 30일 금요일

Shallow Copy / Deep Copy

Shallow Copy / Deep Copy




 .Net 에서는 2가지 데이터 타입이 존재한다.

- Value Teyp- Reference Type


아마도 BCL 을 사용하던, 사용자 정의 클래스를 사용하던 Reference Type 을 사용하는 경우가 대부분 일 텐데, 참조 변수간의 값 할당은 Shallow copy 에 의해서 참조 값이 복사되게 된다. 즉, Managed Heap 에 할당된 하나의 객체를 여러 참조 변수들이 가리키게 된다.


이미지 출처 : C# Object Clone Wars


메모리의 효율적 사용 면에서는 Shallow Copy 에 의한 Clone 을 갖는게 좋을 수 있다. 하지만 경우에 따라서는 독립적인 복사본을 만들어야 되는 경우가 있다.(스레드 사용하면서..)



■ 참조값의 할당 (=)



위와 같이 Person 타입 객체를 참조하는 참조 변수 yuk 가 있을 때 youngho 라는 참조 객체에 yuk 값을 할당 연산자 = 로 값을 대입하면 Shallow Copy 일까, Deep Copy 일까??  난 Shallow Copy 일 거라 추측했지만 Shallow Copy 가 아니다. 단지 yuk 가 참조하던 객체를 youngho 변수도 참조하게 될 뿐이다.



■ Shallow Copy - MemberwiseClone()
Shallow Copy(얕은 복사) 가 참조 타입에 대하여 막연히 객체에 대한 참조를 복사 받는다고 알고 있었는데 Value Type 에 대해 어떤 동작을 하는지는 미처 생각하지 못했다.

1. .Net 에서 Shallow Copy
- 값 타입(Value Type)은 동일한  복사본을 생성
- 참조 타입(Reference Type)은 객체를 가리키는 참조를 리턴 받는다.



즉, Shallow Copy 라고 하더라도, Value Type 에 대해서는 독립적인 메모리를 할당 한다.

.Net 에서 Shallow Copy 를 지원하기 위해 MemberwiseClone() 메소드를 지원한다.



System.Object 의 멤버 이므로 .Net 의 모든 클래스(FCL)들이  MemberwiseClone() 메소드를 갖는다.
protected 접근 제한자를 갖기 때문에 클래스 외부에서 바로 사용할 수는 없고, 클래스 멤버 메소드에서 호출할 수 있다.
사용 예는 다음과 같다.


이미지 출처 :  데브피아 : 객체 복사(깊은 복사/얕은 복사)




■ Deep Copy - ICloneable, Clone()

1. .Net 에서 Deep Copy
- 값 타입, 참조 타입에 관계없이 새로운 복사본을 복사한다.




.Net 에서는 Deep Copy 를 지원하기 위해 ICloneable 인터페이스를 이용한 Close() 메소드를 지원한다.







ICloneable 인터페이스는 System 네임스페이스안에 정의 되어 있으며 Clone() 이라는 하나의 메소드를 갖는다.
요약 설명대로 하나의 인스턴스에 대해서 동일한 새로운 인스턴스를 생성하여 리턴한다. 이때 리턴 타입은 object 타입이다.

MSDN 문서에 따르면 ICloneable 의 Clone() 메소드가 Deep Copy 를 수행한다고 명시적으로 연급되어 있지는 않는다.
cf) C# Object Clone Wars

사용 예는 다음과 같다.


이미지 출처 : 데브피아 : 객체 복사(깊은 복사/얕은 복사)



■ History

1. 2009.10.31 - posting


■ 참조
1. C# Object Clone Wars
2. 데브피아 : 객체 복사(깊은 복사/얕은 복사)

2009년 10월 21일 수요일

[C++/CLI] SEHException...

SEHException...



SEHException 으로 고생중이다... 2주째 인가... ㅠㅠ

예외 메시지는 다음과 같다.








이 예외의 시나리오는 우선

CSocket ← CSocketEx ← CCommandSocket 이라는 상속 구조를 가진 사용자 정의 클래스를 dll 로 만들었다.
해당 dll 을 C# 쪽에서 사용하는데 컴파일 까지는 문제가 없다. 하지만 run-time 에 위와 같은 예외가 발생한다.

dll 에 있는 CCommandSocket 을 마샬링한 클래스 객체를 생성하여 Connect 하는건 성공
또 다른 객체를 생성하여 Connect 하는 것도 성공
하지만 연결된 소켓의 연결을 해제하는 Close() 를 호출하면, 또는 해당 객체를 소멸시키기 위해 Dispose 메소드를 호출하면 그 뒤로는 예외를 발생하면서 서버로 접근이 불가능...서버에 접속하려 하면 위와 같은 예외를 발생 시키며 서버에 접근 불가능..



■ Try

1. 프로젝트 속성 변경

MFC 코드를 마샬링하여 dll 로 만들 때 프로젝트 환경 설정 옵션도 다 해주었다.. 그런데도 또 발생...
http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=8283&ref=8283


2. Application.EnableVisualStyles() Bug

구굴링 해보니 SEHException 키워드로 검색 했을 때 제일 많은 이슈는 "Application.EnableVisualStyles()" 문제이다.
http://www.eggheadcafe.com/forumarchives/win32programmerdirectxmanaged/Oct2005/post23920195.asp
Visual Studio 2005 의 버그 라고 하는데... 내 지금 환경은 Visual Studio 2008 이다. 즉 UI 환경에서의 문제인데 혹시나 하여 WinForm 으로 테스트 하던것을 Console Application 으로 테스트 해 보았지면 역시나 문제 발생...




3. 실행 파일 폴더에 lib 파일 첨부

MFC 코드를 C++/CLI 로 마샬링 할 때는 "공용 DLL에서 MFC 사용" 을 체크 해야 한다고 한다.
혹시나 하여 생성된 dll 파일과 lib 파일을 C#쪽 실행 파일 폴더에 같이 넣어 주었다. 역시나 안됨...



4. C++/CLI 코드의 "코드 분석(정적 분석)" 실행
혹시나 하는 마음에 여기서 나오는 경고도 다 수정해 보자. 컴파일 하니 9개의 경고 메시지가 뜬다.

" warning: CA1707 : Microsoft.Naming : 'CommandSocket::ConnectServer(String^, unsigned int)' 멤버의 매개 변수 이름 '_ip'에서 밑줄을 제거하십시오. "
매개변수의 이름이 밑줄(_)로 시작하게 코딩하였는데 이 방식이 위험성이 있는가 보다. 어떤 경우인지 자세히 생각은 나지 않지만 C# 에서 매개변수가 MSIL 에는 밑줄(_) 로 붙어서 자동으로 생성되게 된다고 들은 것 같다. 메시지 내용처럼 매개 변수 이름에 밑줄(_)을 삭제하면 간단히 해결!


※ Microsoft Design Warnings
정적 분석 결과 중에서 디자인 경고는 MSDN 의 Design Warinings 을 참조하면 된다.
.NET Framework Design Guidlines 에 의해 경고가 되는 목록 들이다.





" warning: CA1823 : Microsoft.Performance : 'context_node_base::_Needs_Context' 필드는 사용되지 않거나 할당만 되어 있습니다. 이 필드를 사용하거나 제거하십시오. "

코드에 전용 필드가 있지만 이를 사용하는 코드 경로가 없는 경우 이 규칙이 보고됨. MSDN 에 따르면 이 경고를 표시하지 않아도 안전 하다고 한다. (Usage Warning)



" warning C4742: 'struct Define_the_symbol__ATL_MIXED::Thank_you Define_the_symbol__ATL_MIXED::clash'의 맞춤이 'stdafx.cpp'과(와) 'CliCommandSocket.cpp'에서 서로 다릅니다(4 - 1). "
" warning C4743: 'struct Define_the_symbol__ATL_MIXED::Thank_you Define_the_symbol__ATL_MIXED::clash'의 크기가 'stdafx.cpp'과(와) 'CliCommandSocket.cpp'에서 서로 다릅니다(4바이트, 1바이트). "


전체 프로그램 최적화(Whole Program Optimization) 옵션 관련 문제이다. 즉 Native Code 를  Static Library 프로젝트로 만든 후, 또 다른 프로젝트를 Mixed Mode dll 로 만들 때, 두 프로젝트의 속성에서 "전체 프로그램 최적화(Whole Program Optimization)" 옵션이 "링크 타임 코드 생성(/GL) : (Use Link Time Code Generation)" 으로 되어 있어서 그렇다. 두 프로젝트의 옵션을 "아니오" 로 선택 하면 위 경고를 없앨 수 있다.



단, 위와 같이 설정 후 컴파일 하면 다음과 같은 메시지를 볼 수 있다.
" LINK : /LTCG를 지정했지만 코드를 생성할 필요가 없습니다. 명령줄에서 /LTCG를 제거하면 링커 성능이 향상됩니다. "
오류도 아니고, 경고도 아니고 조언?! 정도. 일단 바쁘니 패스.

 다른 방법으로 "#define _ATL_MIXED" 을 선언해 주어도 해결 된다고 하는데, 모든 경우에 대한 해결법은 아닌 듯 하다.

참조) Whole Program Optimization, mixed-mode and ATL_MIXED
참조) mixed mode ATL executable warnings



" warning: CA1823 : Microsoft.Performance : 'context_node_base::_Needs_Context' 필드는 사용되지 않거나 할당만 되어 있습니다. 이 필드를 사용하거나 제거하십시오.
warning: CA1812 : Microsoft.Performance : 'context_node<char const *,System::String ^>'은(는) 인스턴스화되지 않은 내부 클래스입니다. 어셈블리에서 해당 코드를 제거하십시오. 이 클래스가 정적 메서드만 포함하도록 의도된 경우에는 전용 생성자를 추가하여 컴파일러에서 기본 생성자가 생성되지 않도록 하십시오.
c:\program files\microsoft visual studio 9.0\vc\include\msclr\marshal.h(366) : warning: CA2201 : Microsoft.Usage : 'context_node<char const *,System::String ^>::context_node<char const *,System::String ^>(const char*&, String^)'이(가) 런타임에서 예약되고 관리 코드에서 발생하면 안 되는 'OutOfMemoryException' 형식의 예외를 만듭니다. 이러한 예외 인스턴스가 throw되면 다른 예외 형식을 사용하십시오. "


참 길기도 하다.. 우선 이 경고는 문자열 마샬링을 위한 #include <msclr/marshal_atl.h> 을 include 해주면 발생한다. MS 에서 문자열 마샬링을 편하게 하라고 만들어 준건데 include 하는 것 만으로도 경고를 주다니..
일단 의심되는 것은 'OutOfMemoryException" 이 발생할 수 있다는 것!
문자열 마샬링 코드를 일단 주석처리하고, #include <msclr/marshal_atl.h> 도 주석처리 해주었다. 다시 정적 분석하면 위와 같은 경고는 발생하지 않는다.

서버 접속을 위한 부분을 하드 코딩 해주었다. 그리고 다시 테스트!



아오... 빡쳐...

ConnectServer 내부적으로 CSocket 을 호출하니 아무래도 CSocket 쪽에서 뭔가 발생하는 듯 하다.




■ 결론

 CSocket 사용상의 문제인 것 같습니다.... (내용을 찾다 보니 MFC 스레드에서 충돌이 일어난다는...)






[C++/CLI] C++/CLI 에서 Value Type 과 메모리 생성 위치

C++/CLI Value Types and Memory Location



C++/CLI 에서 Value Type 은 Stack 에 할당 되거나, Native 또는 Managed Heap 에 메모리 할당 될 수 있다.


다음과 같이 Value Class 를 선언한다.
value class MyData{  property int Simple;};


아래와 같이 변수를 지역 변수로 선언하면 객체는 Stack 에 메모리를 할당한다.
MyData d1;
d1.Simple = 11;



Nativ Pointer 를 사용하여 new 연산자로 객체를 생성하면 메모리는 Native Heap 에 생성된다.
MyData* pd2 = new MyData();
pd2->Simple = 22;
delete pd2;



gcnew 연산자를 사용하여 객체를 생성하면 메모리는 Managed Heap 에 생성되며 이때는 Boxing, UnBoxing 과정이 이루어 진다.
MyData^ d3 = gcnew MyData();
d3->Simple = 33;
delete d3;   // invokes Dispose







출처 : http://weblogs.asp.net/cnagel/archive/2005/02/28/381542.aspx














■ 사용 예


1. C++/CLI 에서의 코드





2. 생성된 DLL 파일을 참조한 C# 에서의 코드







■ History
1. 2009.10.21  포스팅


2009년 10월 2일 금요일

[C++/CLI] Property

Property



 .Net 프로그래밍에서의 Property (속성) 문법 또한 C++/CLI 에서도 사용할 수 있다. C++/CLI 를 알아갈수록 .Net 프로그래밍과 공통점이 많다. 역시 .Net 프로그래밍을 위한 C++/CLI 인것 같다.

property 의 사용 예제 이다.


get() 은 public 만 가능하지만 set() 은 public, protected, private 모두 가능하다.

위 Status 클래스의 property 사용 예이다.



 property 는 class 뿐만 아니라 struct, interface 에서도 사용 가능하다. 또한 virtual, static 으로 선언 가능하다.
Indexed 된 property 또한 사용 가능하며, 다차원으로도 사용 가능하다. 아래 코드는 예이다.



Report 클래스의 Name property 는 set() 의 접근 지정자가 private 이므로 set() 동작은 클래스 내부적으로만 사용 가능하다.
Manager 클래스는 멤버 변수로 reportee 배열을 갖는다.




가장 일반적인 사용 예






■ 참조

1. C++/CLI Primer - Enter the World of .NET Power Programming (CodeProject)
2. Quick C++/CLI - Learn C++/CLI in less than 10 minutes (CodeProject)