미니 프레임 윈도우는 대부분 응용프로그램의 주요 오브젝트인 다른 윈도우들과 함께 존재하는 유동(floating) 오브젝트로서 사용됩니다. 그것은 우리가 예전부터 보아 왔던 것처럼 경계와 클라이언트 영역을 가지는 보통의 프레임 윈도우처럼 보입니다. 그렇지만 미니 프레임 윈도우는 메인 윈도우가 아니라 툴로서 사용됩니다. 이러한 기능의 예를 들자면, 그것은 시스템 최소/최대화 버튼을 가지고 있지 않습니다. 툴 윈도우처럼 그것은 짧은 타이틀 바와 닫기 버튼을 가지고 있습니다.
Creation of a Mini Frame Window
미니 프레임 윈도우는 CMiniFrameWnd 클래스에 기반합니다. 미니 프레임 윈도우를 생성하기 위해서는 먼저 CMiniFrameWnd 에 대한 포인터나 변수를 선언합니다. AfxRegisterWndClass() 함수를 사용해서 그것의 클래스를 정의할 수 있습니다. 그리고 나서 그 함수의 반환값을 Create() 메서드에 넘깁니다. 이 메서드의 구문이 다음에 나와 있습니다 :
dwStyle 매개변수는 윈도우에 적용될 스타일입니다. 일반적으로 그것은 보통 윈도우의 정규 스타일을 사용합니다만, 많은 스타일이 다릅니다. 예를 들어서 Minimize 와 Maximize 버튼을 적용하든 적용하지 않든 그것들은 보이지 않습니다. 단지 다음 조합만을 사용할 수 있습니다 : WS_POPUP | WS_CAPTION | WS_SYSMENU. 정규 윈도우 스타일 이외에도 다음과 같은 특별한 스타일 중 하나 이상을 사용해 조합을 만들 수 있습니다 :
MFS_MOVEFRAME : 이 스타일을 가지면 사용자가 프레임의 어떤 테두리 부분이라도
클릭 & 드래그해서 마치 타이틀 바를 드래그한 것처럼 윈도우를 움직일 수 있습니다. 만약
당신이 이 스타일을 적용한다면, 다른 스타일을 지정해서 윈도우를 리사이즈할 수 있도록
설정했다고 해도 그것은 무시됩니다.
MFS_4THICKFRAME : 이 스타일은 사용자가 미니 프레임을 리사이즈하지 못하게 합니다.
MFS_SYNCACTIVE : 이 스타일은 미니 프레임의 부모가 활성화되어 있을 때
자신도 활성화되어 있도록 보장합니다.
MFS_THICKFRAME : 이것은 두꺼운 프레임을 생성하고 사용자가 필요시 윈도우를
리사이즈할 수 있도록 합니다. MFS_MOVEFRAME 이 무시됩니다.
MFS_BLOCKSYSMENU : 이것은 시스템 메뉴에 대한 접근을 막습니다.
rect 매개 변수는 프레임 윈도우의 위치 및 차원을 지정합니다.
pParentWnd 인자는 윈도우의 CWnd 부모입니다. 이 인자는 필수가 아닙니다.
nID 인자는 미니 프레임 윈도우의 식별자입니다. 이것은 필수가 아닙니다.
Create() 메서드 이외에도 CMiniFrameWnd 클래스는 CreateEX() 멤버 함수를 제공해 미니 프레임 윈도우를 생성할 수 있게 합니다.
[cpp]
void CMiniFrame2Dlg::OnBnClickedMiniframeBtn()
{
// TODO: Add your control notification handler code here
CMiniFrameWnd *MFW = new CMiniFrameWnd;
CString StrClassName = AfxRegisterWndClass(
우리는 컴퓨터 언어의 표준을 따르며 험난한 코딩의 나날을 보내고 있다. 매일 매일 새로운 기술 홍수 속을 키보드와 마우스로 헤쳐나가며, 코딩의 앞길마다 떨어지는 버그를 씹어 삼키며, 코딩의 기술 연마하고 있다.
고임 역시 중후한 표준의 사명을 따르며, 코딩을 하던 중, 컴파일러는 내게 다음과 같은 경고를 보내왔다.
warning C4200: nonstandard extension used : zero-sized array in struct/union (.cpp)
물론 실행하는데는 문제가 없으나 컴파일러가 도전장을 내밀듯이 불쑥 튀어나온 경고는 기분이 나쁘지 않는가?
한동안 위와 같은 경우를 겪어보지 않아. 적잖히 당황을 했지만, 이내 몇 년전 구글의 C 포럼의 쓰레드에서 사이즈가 0인 배열의 글타래를 기억해냈다. 물론 비표준이었는지 아니었는지 기억이 가물가물하여 내가 알고 있는 프로그래머 중 가장 파워풀한 지식과 다이나믹한 코딩 스킬을 가지고 계신 분에게 여쭈어보았다.
고임 : 사이즈가 0인 배열은 표준이 아니죠?
그분 : 표준은 아니지만 대형 벤더들은 전부 지원해요.
그때의 다시금 기억을 되살리려 노력을 한다. 그러나, 금붕어도 울고갈 기억력의 소유자인 고임은 “후후 내가 기억할리가 없잖아!”라며.. 너무 쉽게 인정해버리고 자료를 찾아본다. 그러나 별다른 자료가 없다. 예전에 보았던 구글에서의 쓰레드는 어디로 갔는지 잘 찾질 못하겠고.. 다만 기억이 있는 것은 그 글타래에서 나왔던 이야기 중에 이런게 된다. 정도.. 였던 걸로 기억이 된다. 그 당시에는 사실 별 관심은 두지 않았다.
그리하여 내가 하고 있는 프로젝트에서 해당 경고가 나온 줄을 찾아본다. 그것을 기반으로 하여 사이즈가 0인 배열의 정체를 파혜쳐 보기로 하였다.
이런 식의 강좌 글은 느낌 상으로 수백년만에 다시 써보는 것 같아, 조금은 설레이나.. 할 것이 태산인 상황에서 이런 짓을 하니 조금은 눈치가 보이기는 한다.
허나, 벤더사가 자신있게 내놓은 라이브러리에 비표준이 존재한다는 것에 무슨 의미가 있지나 않을까? 하는 강항 의구심을 해결하기 위하여 이 거대하고도 멋지고도 다 알고 나면 허무 개그를 본 듯한 주제를 가지고 달려보도록 하자.
– 사이즈가 0인 배열의 비표준의 경고와 에러 사이.
사이즈가 0인 배열이라고 하는 것은 단순히 다음과 같다.
int nZeroArray[0];
즉 배열을 선언하는 순간 그 배열의 크기가 0이라는 것을 이야기 한다.
그러나 이것은 비표준이다. 따라서 당연히 에러가 발생이 된다. 상식적으로 크기가 0인 배열을 선언한다는 것이 말이 안되는 것 아닌가? 사이즈가 0인데 뭐하러 선언하는가?
그래서 우리의 컴파일러께서는 다음과 같은 에러 문장을 내주신다.
error C2466: 상수크기 0의 배열을 할당할 수 없습니다.
의역하자면 “장난하냐? 크기가 0인 배열을 어디에 위치시키라고?” 즉, 크기가 없기에 실존할 수 없다는 철학적인 메세지를 주시는 것이다.
여기서 우리가 눈치를 채야 할 것은 바로 사이즈가 0인 배열은 독립적으로 사용할 수 없다 라는 사실이다.
자 반복 학습이 중요하다.
“사이즈가 0인 배열은 절대 독립적으로 사용할 수 없다.”
자 그렇다면 이건 어디서 사용해야하는 것일까?
이것은 눈치가 빠른 분들은 아시겠지만, 구조체 공용체 클래스에서 사용할 수 있다. 뭐 사실 그것 밖에 더 있겠는가? -_-;
그 이유는 나중에 이야기 하기로 하고, 다음을 보도록 하자.
union UZeroArray{
int nZeroArray[0];
};
struct SZeroArray{
int nZeroArray[0];
};
class CZeroArray{
int nZeroArray[0];
};
이 세 개의 구조체, 공용체, 클래스(이거 한글 표현이 뭐지?)안에는 멤버로써 크기가 0인 배열을 갖는다.
이 삼총사는 컴파일링의 시험에 간신히 합격하시며, 다음과 같은 코멘트를 달고 빌드가 되신다.
warning C4200: 비표준 확장이 사용됨: 구조체/공용구조체의 배열 크기가 0입니다.
UDT에 크기가 0인 배열이 있는 경우 복사 생성자 또는 복사 할당 연산자를 생성할 수 없습니다.
이 경고는 봐주긴 봐주는데. 일반적으로 사용하면 죽어!라는 의미를 갖는다. 따라서 사이즈가 0인 배열은 용도가 매우 좁으니까, 너가 신경써서 사용하라는 이야기이다.
자 여기서 강좌 예제의 범위를 축소하기로 한다. 공용체나 클래스의 예제는 과감히 건너뛰기로 하고 구조체에 대해서만 살피도록 하겠다. (알고 싶은 분은 직접 코딩해서 알아보시길.)
struct SEndZeroArray{
int ndummy;
intnZeroArray[0];
};
struct SStartZeroArray{
intnZeroArray[0];
int ndummyEnd;
};
struct SMiddleZeroArray{
int ndummyStart;
intnZeroArray[0];
int ndummyEnd;
};
struct STwoZeroArray{
int ndummy;
intnZeroArray1[0];
intnZeroArray[0];
};
위의 크기가 0인 배열의 예제들이 아주 훌륭히 짜여져 있다. 여기서 컴파일 에러를 내지 않는 것은 무엇일까?
컴파일을 해보면 알겠지만, 첫 번째 예제만이 무난히 컴파일링을 통과하시게 된다. 나머지 세 개의 예제는 다음과 같은 에러를 당하시게 된다.
error C2229: struct ‘SstartZeroArray’에 크기가 0인 잘못된 배열이 있습니다.
즉, 사이즈가 0인 배열의 멤버는 공용체, 구조체, 클래스의 선언에서 가장 마지막에 선언을 해줘야하며, 해당 구조체, 공용체, 클래스에서 유일한 멤버변수여야 한다는 이야기이다. 여기서 매우 중요한 이야기가 두 개나 나오게 된다. 자 기억하자!!
“사이즈가 0인 배열은 공용체, 구조체, 클래스 안에서 가장 마지막 멤버 변수로 선언되어야 한다.”
“사이즈가 0인 배열이 선언이 되는 공용체, 구조체, 클래스 안에서 오직 하나여야 한다.”
여기까지 사이즈가 0인 배열을 에러를 피하기 위해 어떤 식으로 선언을 해야할지를 알아보았다. 세줄로 요약하면 다음과 같다. 이것을 명심하고 다음 단계로 넘어가기로 하자.
“사이즈가 0인 배열은 절대 독립적으로 사용할 수 없다.”
“사이즈가 0인 배열은 공용체, 구조체, 클래스 안에서 가장 마지막 멤버 변수로 선언되어야 한다.”
“사이즈가 0인 배열이 선언이 되는 공용체, 구조체, 클래스 안에서 오직 하나여야 한다.”
– 버그의 경계를 넘어..
자 여기서 부터는 왜 이런 비표준을 여전히 아직도 쓰고 있는지에 대해서 알아보겠다. 라고는 했지만. 역으로 추적하면서 이 강좌를 쓰는 것이므로 역사적인 배경은 전혀 알 수 없다. 그리고 역시 C99이나 새로 계정된 C++ 표준에 반영이 되려 했는지도 알 수 없다. 최근에 보았던 C99에 자료에서는 보지 못했던 것 같다. 사실 이런 기법에 대해서 관심이 없었다고 하는 것이 더 맞는 표현이겠지만, 말이다.
기존에 MS가 제공하는 라이브러리의 헤더에서 보여지는 크기가 0인 배열의 흔적을 거꾸로 찾아보며 느낀 점을 바탕으로 만들어진 예제를 가지고 왜 이런 비표준을 아직도 사용하는지에 대해서 이야기 해보도록 한다. (사실 크기가 0인 배열을 활용할 수 있는 기법은 하나 밖엔 없을 것으로 확신한다. )
자 시작해 보도록 합시다.
아래 예제는 일반적으로 어떤 정보 목록의 크기가 정적으로 정해져 있지 않고, 동적으로 변경이 될 가능성이 높을 때 사용되는 일반적인 정보 목록 관리 방법과 크기가 0인 배열을 이용한 정보 목록 방법을 비교하여, 크기가 0인 배열을 사용하면 어떤 이득이 있는지에 대해 알아보는 시간을 갖도록 하겠다.
다시 쉬운 문장으로 이야기 하자면, 아.. 예전엔 쉽게 잘(-_-;) 썼던 것 같은데. 음…..
예를 들어,
공연 티켓을 사는 사람들의 성향을 알아보기 위한 프로그램을 설계한다고 가정하자.
(여기서 Db를 사용하는 건 제외합시다.)
기본적으로 공연 티켓을 사는 사람들의 정보는 이름, 나이, 구매티켓수, 지역, 구입처 등등이 있을텐데.
구매하는 사람에 대한 정보를 담을 구조체가 필요할테고,
이 정보를 통합 관리할 구조체가 또 필요하다.
바로 이런 경우의 상황에 대해서 앞으로 논의할 것이다.
아래 예제를 보시면서,
“기술적으로 저건 올바른 방법이 아니네.“
“이렇게 해야 더 효율적이네.“
“저게 무슨 일반적인 방법이야?“
“크기가 0인 배열은 됐구요. 도는 믿고 계십니까?“
“저 예제는 어디서 본 것 같아? 당신 표절 쟁이지?“
라고 평하실 분들.. 여기서 읽기를 그만두시길 바랍니다.
더 읽어봤자 서로 정신건강에 해롭습니다.
다시 한번 강조하지만, 이 강좌는 “크기가 0인 배열이 무엇인가?”를 알아보는 것이다.
설명하기 편하게 UNICODE는 사용하지 않았다는 걸 미리 이야기한다.
예제의 빌드 환경
OS : Windows XP Professional
Complier : VS2005
CPU : Intel Core 2 6600
typedefstructtagVirtualDeviceInfo
{
int DummyN;
char DummyString[30];
float DummyFloat;
}VirtualDeviceInfo, * pVirtualDeviceInfo;
Device의 정보를 담을 구조체.
typedefstructtagVirtualDevice
{
int DeviceSize; // 가상장치의 갯수.
pVirtualDeviceInfo Info; // 가상장치의 정보에 대한 주소
}VirtualDevice, *pVirtualDevice;
Device 정보를 통합 관리할 구조체( 일반적인 방법)
이 데이타 구조는 여러개의 가상 장치의 정보를 가상 장치 정보의 주소를 담을 수 있도록 했으며, 데이타 구조에서 관리되는 가상 장치의 정보의 갯수도 기록할 수 있겠금 구성되어있다.
이 구조가 우리가 흔히 접할 수 있는 구조이며, 이런 형태로 구성하는 것이 거의 정석이라 할 수 있겠다.
typedefstructtagVirtualDeviceZeroSize
{
int DeviceSize; // 가상 장치의 갯수.
VirtualDeviceInfo Info[0]; // 바로 이 분이 이 강좌의 주인공이심.
}VirtualDeviceZeroSize, * pVirtualDeviceZeroSize;
Device 정보를 통합 관리할 구조체( 크기가 0인 배열을 이용한 방법)
이 데이타 구조는 2번째의 데이타와 구조와 마찬가지이지만, 가상 장치 정보를 기록하는 멤버 변수가 크기가 0인 배열로 선언이 되어 있다.
처음 보는 사람들은 아마 “이 뭐병” 이란 단말마를 내실지 모르겠으나, 이 코드는 굳건히 컴파일링을 통과하십니다.
01 : voidZeroLengthArrayTest(void)
02 : {
03 :int nDeviceNumber = 2;
여기서 가상 장치가 2개만 있다고 생각하기로 하자.
04 : int nSizeVirtualDeviceInfo = sizeof(VirtualDeviceInfo);
여기서 우리는 VirtualDevice 크기가 8바이트이고, VirtualDeviceZeroSize의 크기가 4바이트 라는 것을 눈여겨봐야한다. VirtualDeivce의구조는Device의 목록의 갯수와 DeviceInfo의 정보를 가리키는 포인터로 구성되어 있으므로, 당연히 8바이트가 되게 된다. (물론 32비트 OS 상에서 32비트 어플리케이션을 제작할때의 이야기이다. 설마 이런 것도 트집 잡으려나? )
VirtualDeviceZeroSize는 Device의 목록의 갯수를 저장하는 변수가 4바이트 DeviceInfo의 정보를 저장하는 배열의 크기가 0이다.
이런 걸 처음 보는 사람은 리엑션을 크게 취해주도록 하자. 그래야 기억에 남는다고 하더라.
허허 그렇담 VirtualDeviceInfo Info[0]; 는 어디에 저장이 될 것인가?
C# Coding Standards and Best Programming Practices ( C# 코딩 표준과 좋은 프로그래밍 습관 )
이라는 제목의 닷넷 스파이더팀( http://www.dotnetspider.com )의 글을 번역한 글입니다.
작년 회사에서 팀원들 대상으로 세미나할때 사용했던 문서인데, 하드에 짱박혀있는것 보단, 많은 분들이 보셨으면 해서 올립니다.
이글의 내용중 ‘권고안’은 말그대로 Let’s 의 의미이며, ‘역주’라고 작성된 이외의 모든 내용은 닷넷 스파이더 팀의 의견임을 미리 밝혀둡니다. (사실 제 의견과 좀 틀린 부분도 있어서요 =ㅂ=)
목차는 아래와 같습니다.
01. 서문
02. 저작권에 관해
03. 이력관리
04. 소개
05. 이문서의 목적(purpose)
06. 팀 내에서 표준을 만드는 방법
07. 명명규칙과 표준
08. 들여쓰기, 공백
09. 좋은 프로그래밍 습관
10. 아카텍처(Architecture)
11. ASP.NET
12. 주석
13. 예외처리