Media Log

마이크로소프트에서 카사블랑카라고 이름지어진 흥미로운 라이브러리를 발표했다.


http_client bing( L”http://www.bing.com/search” );
bing.request( methods::GET, L”?q=S.Somasegar” )
.then( []( http_response response ) {
cout << “HTML SOURCE:” << endl << response.to_string() << endl; })
.wait();


listener::create( argv[1], []( http_request req ) {
req.reply( status_codes::OK, “Namaste!” ); })
.listen( []{ fgetc( stdin ); } )
.wait();


여기 공식 페이지에 간략하게 소개가 있으며, 허브 셔터가 자신의 블로그에 따로 소개해주기도 하였다.

아마 Restful api를 제공하는 서비스들의 클라이언트 코드로써 가장 많이 사용되지 않을까 싶다.

 

얼마전 있었던 마이크로소프트의 Going Native 2012 행사에서 허브 셔터가 말하길,

C++ 언어는 다른 최신 언어들에 비해 부족한 점이 거의 없다. 부족한 것은 바로 라이브러리이다. 라는 말을 했었는데 아주 인상 깊게 들었다. 프리젠테이션 자료를 너무 인상적으로 만들어서 더욱 설득 당했는지도 모르겠다.

아래 주소에서 그 동영상을 볼 수 있다. 1시간 17분 쯤부터 보기 시작하면 된다.

http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/C-11-VC-11-and-Beyond


어쨌거나 좋은 C++ 라이브러리들이 빨리 빨리 구현되기를 바란다. 그래야 Going Native Again 할 것 아닌가.



 

저작자 표시 비영리 동일 조건 변경 허락
신고

submit
이거 당황스럽군요.

몇 일전 Going Native 2012 키노트 발표에서, 적절한 자료구조와 알고리듬을 사용하라는 이야기를 하던 중 준비한 프리젠테이션 자료가 한장 빠진 것을 깨닫고서는.

비야네의 예기치 못한 방송사고(?)에 긴장해서 지켜보다가 그의 대처에 폭소하여 쓰러질 뻔했다.

저작자 표시 비영리 동일 조건 변경 허락
신고

submit
C++에서 함수 이름이 맹글링 되는 것은 다들 알고 있는 사실일 것이다.
링커 오류를 만나게 되면 원인을 분석하기 위해 때때로 함수의 오리지날 이름이 알고 싶을 때가 있다. 마이크로소프트의 도구로 만들어진 함수들에 대해서는 undname 이라는 프로그램이 바로 그런 일을 해준다.

레이몬드 첸의 한 포스트를 읽다가 그는 단순한 함수들에 대해서는 undname을 사용하지 않고도 한눈에 척 알아볼 수 있는 것 같다는 느낌을 받았다.
어떻게 그런 일을 할 수 있는지 알아보다가 강성훈씨가 쓴 이 포스트를 보게 되었다. 그가 말하는 것처럼 아마 현재까지 가장 잘 정리된 자료라는데 동의한다. 어린 나이에 정말 대-_-단 하다고 밖에는 말할 수 없다.

...하지만 나는 한참 살펴보다가 결심했다.
'빌어먹을. 나는 그냥 undname이나 써야겠다.'

흐아, 생각처럼 간단한 일이 아니었다.
그런데 레이몬드 첸은 정말 간단한 함수 정도는 추측할 수 있는 것일까? 아직도 잘 모르겠다.
저작자 표시 비영리 동일 조건 변경 허락
신고
  1. gcd at 2012.02.22 06:38 신고 [edit/del]

    msvc로 mangling 당한 이름에 처음 접했을 때 꽤나 당황했었지요.
    저 룰도 컴파일러마다 다른 모양이라 (거기에다 msvc 9.0부터 규칙-이라기보단 인코딩-이 바뀌었다고 하고...) 링크 시간에 호환이 되긴 하나 하는 의문도 들더라구요.
    여튼, 저 암호(?)를 풀 수 있다는 사실을 알았던건 Dependency walker를 써보면서였군요. 포스트와 다르게 DLL 파일의 export/import된 함수들을 볼 수 있지만, C++ 로 만든(그것도 msvc로 컴파일 한) 몇 비공개 라이브러리에 들어있는 멤버 함수들을 다 볼 수 있어서 신났던 기억이 나네요.

    Reply
    • Favicon of http://www.benjaminlog.com BlogIcon 김재호 at 2012.02.22 10:22 신고 [edit/del]

      저는 9부터 바뀐줄 몰랐습니다. 그런데 그런 걸 바꾸면 부작용이 엄청날텐데. 호환성은 어떻게 맞춰줬을지 궁금하군요.

    • gcd at 2012.02.22 21:19 신고 [edit/del]

      룰이 바뀌었다기보단, 인코딩 처리하는 방법이 ANSI에서 UTF-8로 바뀌었다고 하더라구요.
      대부분 영문 명칭을 쓰기 때문에 임팩트가 거의 없을 것입니다.
      아니, 임팩트가 없었으면 좋겠는데, 8.0 이전에 만든 obj와 이후에 만든 obj 사이에서 가끔 링크 오류가 나긴 하나보더라구요. msvc 8.0용으로 땜빵(?) 패치가 나왔었고(이것도 주로 일본어권의 요구사항으로... 그러고보니 바뀐건 9가 아니라 8부터였네요.) 9.0 이후로는 지원 계획이 없다고 하네요.

      적고보니 mangling이랑 별로 상관 없네요. 헷갈리게 한 것 같아서 죄송합니다.

submit
C++에서는 멤버 함수에 const 키워드를 사용할 수 있다. 이는 메서드 내에서 멤버 변수들의 값을 바꾸지 않겠다는 약속이다.
void Clazz::foo() const
{
}
위의 const 변경자는 해당 인스턴스의 this 포인터에 영향을 끼치게 된다. 즉 멤버 함수 내에서 this 포인터의 타입은 const Clazz* 가 된다. 그러므로 해당 멤버 함수 내에서 멤버 변수의 값을 바꾸려고 하면 컴파일 에러가 발생한다.

static void Clazz::boo() const
{
}
하지만 static 멤버 함수에 대해 const를 붙일 경우에는 컴파일 에러가 발생한다. 그 이유는 static 멤버 함수는 this 포인터를 가지고 있지 않기 때문이다. this가 없는데 어떻게 this를 const로 만들겠는가.
그래서 질문에 대한 답은 '아무런 의미가 없는 짓이기 때문' 이라고 할 수 있겠다.
저작자 표시 비영리 동일 조건 변경 허락
신고

submit
MSDN에 따르면 구조체의 디폴트 패킹 값은 8이다. 간혹 32비트 운영체제에서는 4바이트이고 64비트 운영체제에서는 8바이트라고 주장하는 사람들도 있는데 디폴트 패킹 크기는 컴파일러가 결정하지 운영체제가 결정하는 것이 아니다. MSVC에서 디폴트 패킹을 8바이트로 정한 이유는(32비트 운영체제에서 조차) 기본 타입 중 가장 큰 타입이 8바이트이기 때문이다. 만약 이후에 16바이트 포인터나 INT128 같은 타입을 기본 타입으로써 사용하는 날이 온다면, 그 때는 디폴트 패킹 값도 16바이트로 변경될 것으로 예상한다.

여기에 패킹을 잘 이해하고 있는지 알아보기 위한 좋은 질문이 있다.

strcut X
{
  char c1;
  char c2;
  char c3;
  char c4;
  char c5;
  char c6;
  char c7;
};

struct Y
{
  char c;
  double d;
  int i;
};

디폴트 패킹 값인 8을 사용한다고 할 때 구조체 X와 Y의 크기는 각각 얼마일까?

잠시 생각해보고 아래를 클릭해서 답을 확인해보도록 하자.

더보기


구조체의 멤버들은 자신의 크기의 배수로 정렬되는 것이 좋다. char는 1의 배수, short은 2의 배수, int는 4의 배수, double은 8의 배수의 메모리 번지 주소에 위치하고 있을 때 우리는 해당 데이터가 정렬되어 있다고 말한다.
x86호환 아키텍쳐에서 윈도우즈 응용 프로그램을 만들 경우에는 정렬이 되어있지 않을 때 CPU가 메모리에 다시 접근하려고 시도하면서 성능이 떨어지게 된다. 다른 아키텍쳐에서는 응용이 크래시가 나거나 따로 예외 핸들링을 해주어야 할 수도 있다.

컴파일러는 데이터를 정렬시키기 위해서 구조체의 적당한 위치에 패딩을 집어넣는다. 조금 생각해보면 위 Y구조체에 마지막 4바이트 패딩은 필요가 없을 것 같다. 중간에 넣은 7바이트 패딩으로 인해 3개의 필드가 모두 잘 정렬이 된 것 같은데 말이다.
뒷 부분에 4바이트 패딩을 넣은 이유는 구조체가 배열에서 사용될 때 구조체의 멤버들이 메모리의 정렬된 위치에 올라가도록 하고 싶기 때문이다. 뒷 부분에 패딩을 넣지 않았으면 int나 double 타입이 자신의 타입에 맞게 정렬된 주소에 올라가지 못했을 것이다.

다음 Z구조체를 보자. 위의 Y구조체에서 double과 int의 위치만 바꾸었다. -위치만 바꾸었는데 패딩이 Y구조체와 다르게 들어가는 것에 대해서도 유심히 살펴 보아야 한다.
struct Z
{
    char c;
    // pad[3]
    int i;
    double d;
};

int _tmain(int argc, _TCHAR* argv[])
{
    // 다음 코드를 사용해서 어떻게 padding이 들어가 있는지 쉽게 확인해볼 수 있다.
    printf("position c:%d\n", FIELD_OFFSET(Z, c));
    printf("position i:%d\n", FIELD_OFFSET(Z, i));
    printf("position d:%d\n", FIELD_OFFSET(Z, d));
    printf("Total size:%d\n", sizeof(Z));
}
직접 코드를 실행시켜보는 것도 좋고, 아래 그림을 보고 이해해도 좋다. 이 구조체가 배열에서 사용될 때에는 아래와 같은 레이아웃을 갖게 될 것이다.

char는 1의 배수에, int는 4의 배수에, double은 8의 배수에 정렬되어져 올라가 있는 것을 주목하라. 진한 파란색으로 표시된 3바이트 패딩이 있기 때문에 가능한 일이다.

맨 처음 문제에서 X구조체의 크기가 8bytes가 아니라 7bytes인 이유는 모든 멤버가 char이기 때문이다. char는 1의 배수인 어느 주소에나 올라가도 되므로 padding을 집어 넣지 않아도 모든 멤버가 항상 자신이 원하는 주소에 올라가게 된다.
Y구조체가 Z구조체와 멤버 위치만 바꾸었는데 다른 레이아웃을 가지고 있는 이유도 그림을 그리면서 확인해보면 이해할 수 있을 것이다.

8로 패킹한다는 것은 구조체의 크기를 8의 배수로 맞추겠다는 것이 아니라, 크기가 8보다 큰 멤버가 있을 때는 정렬을 포기한다는 것을 뜻한다. 즉, 크기가 8보다 작은 타입에 대해서만 정렬하려고 시도하며, 이것은 다른 말로, 변수의 메모리 주소를 최대 8의 배수로 정렬한다는 뜻이 된다.
만약 패킹 크기를 4로 바꾼다면 double이나 int64_t 같은 타입들이 사용되었을 때 더 이상 정렬이 보장되지 않게 된다. 왜 디폴트 값을 8로 정했는지 이해가 되는가?

구조체를 만들 때는 어떻게 패킹이 될지 잘 예상해서 조각을 맞추듯이 만들어야지 아무 순서로나 마구 쑤셔넣는 것은 프로답지 못하다. 마이크로소프트에서 만든 거의 대부분의 구조체들은 이런 사소한 것들까지 잘 고려해서 만들어져 있다.
저작자 표시 비영리 동일 조건 변경 허락
신고
  1. 황후순 at 2011.12.19 09:02 신고 [edit/del]

    틀린 내용인거 같은데... double이 많을까요? Pointer가 많을까요?
    포인터가 32비트에서 4바이트고 64비트 8바이트라서 패킹을 각각 하고 있습니다. 구조체 크기랑은 별개의 이야기지요.메모리크기는 당연히 데이터 사이즈대로 나오겠죠. 패킹과 메모리사이즈는 별개의 얘기죠.
    단편화 생기는 과정을 만들어서 프로그램이 어떻게 죽는지 확인해보시기 바랍니다.
    필자는 ms vs compiler만 확인해보신 것이 아닌지... 메모리 패킹은 운영체제가 변경됨에 따라도 얼마든지 바뀔수 있으니 무조간 8바이트라고 단정하는건 문제가 됩니다.

    Reply
    • Favicon of http://www.benjaminlog.com BlogIcon 김재호 at 2011.12.19 10:10 신고 [edit/del]

      패킹이랑 메모리 사이즈는 별개의 이야기가 아닙니다. 그리고 패킹은 운영체제와는 상관없는 이야기이고요.
      무조건 8바이트라고 단정한 것이 아니라 왜 기본값을 8바이트로 정했는가에 대해서 말해본거에요.

      프로그램이 어떻게 죽는지 확인해보라는 말을 조금만 더 자세히 설명해 주실수 있을까요? 무슨 이야기를 하고 싶으신건지 궁금하네요^^

  2. kim at 2014.05.08 09:10 신고 [edit/del]

    돌아다니다 여기까지왔네용.
    .
    글 잘봤습니다.

    제가 내린 결론은 운영체제가 몇비트 머신인지.. 그리고 컴파일러가 뭐인지에 따라
    패킹바이트가 달라진다는 결론을 얻었습니당...

    맞나요 ?!

    Reply
    • Favicon of http://sunyzero.tistory.com BlogIcon sunyzero at 2014.05.12 16:29 신고 [edit/del]

      패딩은 XDR 표준과 관련이 깊습니다. 단지 컴파일러가 CPU를 효율적으로 쓰기 위해서만은 아닙니다.

      이는 CPU의 효율면에서만 정한 것이 아니라 잠재적으로 다른 머신과의 통신(심지어 호스트 내부 통신이라고 할지라도...)에 정렬 오류를 없애기 위해서 제정되었습니다.

      과거에는 희귀한 64비트 머신인 크레이을 제외하였고, 64bit는 32bit 정렬을 포함하기 때문에 RFC에서는 4Byte 정렬을 기준으로 내세우고 있습니다. 따라서 double형을 쓰지 않는다면 대개 표준에서 지정한 4Byte정렬을 사용하고 있습니다.

      RFC문서를 보시면 좀더 빠르게 이해하실 수 있습니다. 원래는 RFC1014였다고 1832로 리바이스 되었습니다. (discussion부분을 보시면 왜 4B를 표준에 사용했는지 나오고 있습니다.)
      http://tools.ietf.org/html/rfc1832

submit


훌륭한 프로그램을 짜려면 똑똑한 머리, 감각적인 눈, 그리고 묵직한 엉덩이가 있어야 한다. 이 규칙들은 눈과 머리만으로는 결코 이해할 수 없다. 직접 부딪혀 봐야 비로소 몸에 스며드는 것을 느낄 수 있을 것이다. - TCPL 특별판 67 페이지에서

저작자 표시 비영리 동일 조건 변경 허락
신고
  1. fullc0de at 2011.12.20 23:23 신고 [edit/del]

    오~ 이말 정말 와닿네요.. ㅎㅎ

    Reply

submit


나는 이 책의 0x 버전을 읽었는데, 얼마전에 정식판이라고 할 수 있는 C++11 버전이 발표되었다.
거의 모든 장이 아래 그림 처럼 코드 조각들로 이루어져 있고 스캇마이어스의 짧은 설명들로 보충된다.
예제 코드들이 궁금했던 점들을 너무도 잘 긁어주기 때문에 C++11의 새로운 기능들을 빠르게 익히는데 도움이 많이 되며 영어 때문에 부담스러워하지 않아도 된다.


저작자 표시 비영리 동일 조건 변경 허락
신고
  1. 비제이 at 2011.11.22 01:13 신고 [edit/del]

    오오 이 책 꼭 봐야되겠네요. 고맙습니다^^

    Reply
  2. 초보 at 2011.12.05 15:48 신고 [edit/del]

    이 책 어디에서 살 수 있나요?
    국내 인터넷 서점은 물론 아마존에도 없는 것 같은데요.
    꼭 보고 싶어요.

    Reply
  3. 초보 at 2011.12.05 17:16 신고 [edit/del]

    감사합니다.

    Reply
  4. 리커버리 at 2012.03.19 00:10 신고 [edit/del]

    오 좋은책 소개 감사합니다~ 꼭 봐야겠어요~^^

    Reply

submit
이펙티브 C++ - 10점
스콧 마이어스 지음, 곽용재 옮김/피어슨에듀케이션코리아

한 동안 계속 C로만 코드를 짜다가 최근 회사를 옮기면서 C++를 다시 하고 있는데 C++로 코딩하는게 그렇게 재미있을 수가 없다.

이 책은 2006년도에 회사에 갓 들어가서 한 번 읽었던 책인데, 최근 읽은 Effective STL이 너무 재미 있어서 5년여 만에 다시 꺼내어 읽어 본 책이다.
두번째로 읽었지만 그 당시 읽었던 것보다 더 재미있게 읽었고 정말 많은 부분을 놓치고 지나갔었다는 것을 알게 되었다.

이 세상에 스캇 마이어스처럼 C++를 재밌고 간결하게 설명할 수 있는 사람이 있을까? 있을수도 있지만 아마 비야네 스트롭은 아닐 것이다. 비야네의 책은 좋은 책임에는 분명하지만 정말 재미없고 어렵다. -.-

책을 읽다가 문득 궁금했다. 혹시 이제 C++11 버전 에디션을 하나 내지는 않을까? 어쩌면 이미 준비하고 있는 것은 아닐까?
그래서 한 번 메일을 보내서 물어봤는데, 맙소사 스캇 마이어스가 내게 답장을 해주다니.

안타깝게도 아직은 계획에 없다고 한다. 새 버전의 책은 분명히 쓸만한 가치가 있지만 C++11을 Effective하게 사용하는 법을 쓰기 위해서는 먼저 그에 대한 충분한 경험이 필요하기 때문이라고 한다.
뭔가 새 버전만 나오면 남에게 뒤질세라 대충 공부해서는 얼른 책 한권을 만들어 내는 요즘 세상에 참 멋진 해커가 아닌가?
아마도 스캇 마이어스가 말하는 충분한 경험이란 꼴랑 두세달 공부하는 것은 아닐 것이다.
오래 기다려야 할 것 같아서 안타깝긴 하지만 언젠가는 꼭 다음 에디션이 나오게 되지 않을까 싶다.
저작자 표시 비영리 동일 조건 변경 허락
신고
  1. kwon at 2011.08.24 16:28 신고 [edit/del]

    저도 읽을 때마다 재미나게 읽고 놓쳤던 부분을 항상 찾게 되서 읽게 되는 책이예요~ 보물같은 책!!!ㅋ

    Reply
  2. 방문자 at 2013.07.01 00:51 신고 [edit/del]

    c++11버전을 준비중이라고 합니다.
    지나가던 길에 소식 전하고 갑니다.

    Reply

submit
이펙티브 STL - 10점
Scott Meyers 지음, 곽용재 외 엮어옮김/정보문화사
STL은 정말 잘 만들어진 라이브러리라고 생각한다. 어쩌면 C++로 만들어진 가장 아름다운 코드가 아닐까.
많은 사람들이 STL을 컨테이너를 위한 용도 정도로만 사용하는데 사실 STL의 컨테이너들은 알고리즘과 함수 객체와 함께 조합될 때 더 큰 위력을 발휘하고 바로 이것이 STL을 사용하는 재미가 아닌가 싶다.

스캇 마이어스의 책은 모두 다른 설명이 필요 없는 최고의 책이다. 몇 일동안 출근 길에서 이 책을 읽으면서 너무 즐거웠다. 스캇 마이어스가 C++를 설명하는 방식과 유머 감각은 정말 아무도 따라할 수 없을 것 같다.
Effective C++와 More Effective C++를 다시 한 번 읽고 싶은 마음이 생겼다.

번역 또한 완벽한데, 스캇 마이어스가 농담하는 것까지도 우리 말로 자연스럽게 잘 표현되어 매 챕터마다 나를 큭큭 웃게 만들었다.

STL을 배우기 위해서는 우선 컨테이너, 반복자, 함수객체에 대해서 개념적으로나 문법적으로 이해를 하는 것이 중요한데 이 책에서는 그런 내용을 자세하게 다루어주지 않는다.
아직 STL을 한 번도 접해본 적이 없다면 이 책을 읽기 전에 먼저 C++ Standard Library를 읽어보는 것을 추천한다.
저작자 표시 비영리 동일 조건 변경 허락
신고
  1. 고구마 at 2011.08.05 07:53 신고 [edit/del]

    안녕하세요. 저는 원래 다른 전공을 하다가 다시 프로그래머로 전향하게 된 사람입니다. 우선 감사하다는 말씀을 드리고 싶네요. 이 블로그를 통해 소개해 주신 좋은 책들을 알게 되어 얼마나 다행인지 모릅니다. 책은 무척 좋아하지만 아무래도 이쪽 전공이 아니다보니 아는 사람이 별로없어 무슨 책을 보고 어떻게 공부할지 무척이나 막막했기 때문입니다. 감사한 마음을 전하고 싶어 이렇게 글 남깁니다. 앞으로도 풍성한 블로그가 되길 바랍니다. 감사합니다 ^^

    Reply

submit

_countof 매크로

2011.03.15 06:48 | Programming
_countof 매크로는 배열의 원소 개수를 돌려주는 서비스 매크로이다. 비주얼 스튜디오 2005 부터인가 제공되었던 것 같다.
StringCchCopystrcpy_s, wcscpy_s 같은 함수들을 사용할 때 편리하게 사용할 수 있다.

_countof 매크로는 다음과 같이 생겼다.
#if !defined(_countof)
#if !defined(__cplusplus)
#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
#else
extern "C++"
{
template <typename T, size_t N>
char (*__countof_helper(UNALIGNED T (&_Array)[N]))[N];
#define _countof(_Array) (sizeof(*__countof_helper(_Array)) + 0)
}
#endif
#endif

C언어를 사용할 때와 C++를 사용할 때의 구현이 다르게 되어있다.
C언어에서 사용되는 방식은 대부분의 사람들이 잘 알고 있는 방식일 것이며, C++에서는 템플릿을 이용해서 배열의 개수를 구하고 있다.

여기서 궁금해해야 할 점은
  1. 템플릿을 통해 어떻게 배열의 원소의 개수를 구할 수 있는가.
  2. 도대체 왜 C와 C++을 전처리기를 사용해서까지 따로 구현했을까. 그냥 C 구현 하나만 쓰지.

1번 질문의 답은 아래 블로그에 잘 설명이 되어있다.
C++ 템플릿으로 배열의 원소 개수를 구하는 방법

2번 질문의 답은 C 구현 방식에 약간의 문제가 있기 때문이다.
배열이 아니라 포인터일 경우에 C 방식은 제대로 개수를 구해주지 못한다.
그럼 템플릿 방식은 제대로 구해주냐 하면 물론 그럴수는 없다. 그래도 컴파일 에러를 내주기 때문에 좀 더 낫다. C 방식은 컴파일이 잘 되어버리며 잘못된 결과를 돌려준다.
아래 코드를 한번 보자. 설명을 쉽게 하기 위해 매크로 이름을 바꿨다.
#define _countof_c(_Array) (sizeof(_Array) / sizeof(_Array[0]))

template <typename T, size_t N>
char (*__countof_helper(T (&_Array)[N]))[N];
#define _countof_cpp(_Array) (sizeof(*__countof_helper(_Array)) + 0)

void c_version(int* p) // int ar[] 과 같은 형식으로 넘어올 때도 마찬가지이다.
{
    printf("%d\n", _countof_c(p));
}

void cpp_version(int* p)
{
    printf("%d\n", _countof_cpp(p));
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a[100];
    printf("%d\n", _countof_c(a)); // 100 올바른 결과
    printf("%d\n", _countof_cpp(a)); // 100 올바른 결과

    c_version(a); // 1 틀린 결과를 내어줘버렸다. 다행히 경고(C6384)는 발생한다.
    cpp_version(a); // cpp_version함수는 컴파일이 안된다.

    return 0;
}

여기서 배워야 할 점은,
  • C 방식 매크로를 쓸 때 언제 문제가 되는지를 알고 있어야 한다.
  • C++를 사용함에도 불구하고 해당 매크로를 C방식으로 직접 만들어서 사용하는 경우를 보았다. 그냥 _countof를 사용한다.
  • 마지막으로, 컴파일러 경고가 발생하면 무시하지 않는다.
또한, 사실 이는 _countof 의 MSDN 페이지에도 잘 나와있는 내용이다. MSDN을 읽을 때는 꼭 개요부터 See Also까지 다 읽는 습관을 들여야 한다. 특히 Remarks 섹션은 유의해야 할 사항들이나 사용자 쪽에서 궁금해 할만한 내부 구현 방식들을 다루어주므로 항상 눈을 부릅뜨고 읽어봐야한다.
저작자 표시 비영리 동일 조건 변경 허락
신고

'Programming' 카테고리의 다른 글

윈도우 드라이버를 만들 때 알아야 할 기초적인 내용들  (2) 2011.05.23
Duff's Device  (0) 2011.04.07
_countof 매크로  (0) 2011.03.15
FIELD_OFFSET 매크로  (1) 2011.03.01
PAGED_CODE 매크로  (5) 2011.02.27
디렉터리의 읽기 전용 속성  (4) 2011.02.20

submit
나는 디바이스 드라이버를 개발할 때, 마이크로소프트의 Prefast for drivers 를 몹시 애용한다.
내가 미처 생각하지 못했던 실수들을 잘 발견해서 가르쳐주기 때문에, 나는 코딩할 때는 항상 경고레벨을 최고로 둠과 동시에 빌드시마다 Prefast 를 자동으로 실행시켜서 경고가 발생했는지 확인하고는 한다.

이렇게 함으로써 버그를 잡을 수 있을 뿐 아니라 코드를 조금 더 정교하고 튼튼하게 만들수 있고 여러 좋은 습관들까지 몸에 배이도록 할 수있다.
이것은 거의 공짜로 주워먹는 큰 이득이기 때문에, 쓸만한 정적 분석툴을 가지고 있다면 무조건 적용시키는 것이 좋다.

나는 Prefast를 유저모드에서도 사용할 수 없을까 찾아보다가 해결책을 찾지 못하고 cppcheck라는 다른 툴을 알아보게 되었다.

cppcheck는 멀티 플랫폼에서 사용할 수 있도록 만들어졌고 오픈소스이며, 분석 능력도 그럭저럭 만족스럽다.
어떤 것들을 분석해주는지는 웹사이트에 잘 나와있으며, 안 읽어보더라도 조금 쓰다보면 금방 알게된다.

GUI 버전으로 실행할 수도 있고, 콘솔 프로그램만으로도 결과를 얻을 수 있는데 나는 Visual Studio의 External Tools 기능을 이용해서 콘솔 프로그램만을 구동시킨다.


위 처럼 적어넣으면 내가 원할 때마다 한번씩 쉽게 분석 작업을 수행할 수 있다.
빌드 후 이벤트에 넣어서 매 빌드마다 실행되게 할 수도 있지만, 모든 프로젝트에 각각 적용시키려면 귀찮은 짓을 많이 해야한다.

External Tools 는 전역 기능이기 때문에 한번만 적어놓으면 가지고 있는 모든 VC 프로젝트에서 바로 사용이 가능하다.

나는 비주얼 스튜디오 아웃풋 윈도우로 출력을 돌리도록 설정하고 사용하는데, 이렇게 할 때는 --template vs 옵션을 주는 것이 좋다.
이 옵션을 넣으면 더블클릭을 통해 해당 소스 코드 위치로 바로 이동할 수 있도록 메세지를 다시 포맷팅 해서 출력해준다.

정말 쓸데없는 경고는 disable 시켜야 하는데, 편리하고 정교하게 disable 시킬수 있는 인터페이스가 아직 없다. 이런 것들은 앞으로 더 개선될 것으로 기대한다.

기존에 이런 종류의 컴파일러 경고들에 무심했었다면, 미친듯이 쏟아지는 경고메세지를 보고는 바로 이 짓을 때려칠지도 모르겠다.

"도대체 누가 경고레벨을 최대로 올리고 코딩을 해?"
"이런 경고 메세지들은 다 쓸데없는 것들이야. 코딩 잘하는데는 도움이 하나도 안돼"

이런 썩어빠진 마음가짐은 빨리 버려야 한다.
자신이 짠 코드에서 경고가 우르르 쏟아져나온다면 분명히 문제가 있는 것이다.

공짜로 얻을 수 있는 이득을 좋아한다면 한번씩 시도해보자.
신고
  1. 지나리 at 2010.08.06 23:34 신고 [edit/del]

    와우 끝내주는군요. 잘 보고갑니다 ㅎㅎ

    Reply
  2. Favicon of http://nyolong.egloos.com/ BlogIcon 뇨릉 at 2010.08.11 20:23 신고 [edit/del]

    저도 Warning 레벨 4에 경고를 에러로 처리하도록 해놓고 하곤 하는데요.
    에러 메시지에 당황하는 분들도 계시더군요.
    아직까지 정적코드분석툴은 안써봤었는데 이제 써봐야 겠습니다.

    Reply
    • Favicon of http://www.benjaminlog.com BlogIcon 김재호 at 2010.08.11 21:46 신고 [edit/del]

      네, 경고를 에러로 처리하고 계신다면 정적분석툴도 틀림없이 좋아하실꺼에요. Visual Studio Team Suite 이상 버전 부터는 정적분석기가 내장되어있습니다.

  3. 곽재상 at 2011.03.21 16:31 신고 [edit/del]

    자바 소스 분석툴 찾다 보니 구글에 낯익은 이곳까지 오게 되네요 ㅋㅋ 올때마다 홈페이지 UI가 계속바뀌네요?? 잘 보고 가요~~

    Reply
    • Favicon of http://www.benjaminlog.com BlogIcon 김재호 at 2011.03.21 21:37 신고 [edit/del]

      그래 재상아. 검색해서 오지 말고 종종 놀러와. 크크^^
      영신이 회사 얼마전에 서울대입구로 옮겼더라. 같이 또 저녁밥이나 먹자구.

submit
Jeffrey RichterWindows via C/C++ 예제 코드에는 공통 헤더파일이 있는데, 이 곳을 살펴보면 유용하게 사용할 수 있을만한 팁들이 많이 있다.

그 중 가장 쉽고 편하게 쓸 수 있는 기능 하나를 소개하고자 한다.

코드를 작성하다가, '이 부분은 나중에 고쳐야지' 하고 주석으로 마킹해 놓은 뒤에 나중에 잊어버리고 그대로 릴리즈했던 경험이 있는 사람이라면 이 매크로를 아주 좋아하게 될 것이다.

그의 예제코드 중 CmnHdr.h 라는 파일을 보면 다음과 같은 코드가 있다.

//// Pragma message helper macro ////
/* 
When the compiler sees a line like this:
   #pragma chMSG(Fix this later)

it outputs a line like this:

  c:\CD\CmnHdr.h(82):Fix this later

You can easily jump directly to this line and 
 examine the surrounding code.
*/

#define chSTR2(x) #x
#define chSTR(x)  chSTR2(x)
#define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc)


주석에 잘 쓰여 있듯이 Pragma 지시어를 이용해서 코드 어떤 부분에,
#pragma chMSG(나중에 고칠 것)
int c = a + b;
이런 식으로 주석 대신 적어두는 것이다.

이제 빌드를 하게 되면, Output 창에 이 메세지가 나타나게 되므로 실수를 줄일수 있다.
또한 에러나 경고 메세지와 같이, 더블 클릭 하게되면 해당라인으로 바로 이동하게 된다. 이것은 pragma message의 기능이 아니라, Jeffry가 매크로에 파일과 라인수를 Output창이 알아볼 수 있는 형태로 잘 정의해두었기 때문이다.

하지만 그럼에도 불구하고, 빌드 되는 동안 다른 경고들이 화면 가득 나와서 아예 경고 메세지를 쳐다보지 않는 사람들에게는 혜택이 없다.

이 지시어는 아주 유용하긴 하지만 나는 조금 더 쓰기 편하도록 다음과 같이 매크로로 고쳐서 사용하고 있다.
 
#define chSTR2(x) #x
#define chSTR(x)  chSTR2(x)

#define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "): --------" #desc "--------")
#define chFixLater message(__FILE__ "(" chSTR(__LINE__) "): --------Fix this later--------")

#define FixLater \
    do { \
    __pragma(chFixLater) \
    __pragma (warning(push)) \
    __pragma (warning(disable:4127)) \
    } while(0) \
    __pragma (warning(pop))

#define MSG(desc) \
    do { \
    __pragma(chMSG(desc)) \
    __pragma (warning(push)) \
    __pragma (warning(disable:4127)) \
    } while(0) \
    __pragma (warning(pop))



우선은 코드 중간 중간에 #pragma를 쑤셔넣는 것이 보기가 싫었는데, 이 pragma를 매크로 안으로 넣어버렸다. MSVC에는 __pragma라는 키워드를 사용할 수 있는데, 매크로 안에서 pragma 지시어을 사용하기 위해 고안되었다.
만일 예전에 매크로를 만들다가 매크로 안에 #pragma 지시어까지 넣을 수 없을까 고민했던 적이 있던 사람에게는 아주 좋은 소식일 것이다.

또 하나는 코드 맨 끝에 세미콜론을 붙여야 컴파일 되도록 강제하였다. #pragma 지시어는 C문법이 아니므로 세미콜론을 써줄 필요가 없는데, 코드 중간 중간에 들어갈 매크로인만큼 세미콜론이 없으면 미관상에도 안좋고, 복사해서 붙여넣기 등을 할 때 들여쓰기가 깨져버리는 문제가 있다.

그래서 보통 매크로를 만들 때는 세미콜론을 꼭 붙여야 정상적으로 컴파일 되도록 작성하는 것이 좋은데, 위 매크로에서는 do while 얍삽이를 통해서 세미콜론을 강제하고 있다.
저 얍삽이는 Ace 프레임워크의 ACE_DEBUG 매크로를 살펴보다가 알게 된 것인데, 세상에는 참 얍삽하게 머리 좋은 사람들이 많구나 하는 생각을 했다.
농담이다.

do while 얍삽이를 쓰게되면, while(0) 때문에 경고가 발생하는데, 이 역시 __pragma로 감싸버려서 없앨 수 있다.

마지막으로 앞뒤로 ----를 붙여서 좀 더 눈에 띄기 쉽도록 하였다.

이제 다음과 같이 사용할 수 있다.

#pragma chMSG(블라블라블라)
int main()
{
    FixLater;
    int a;

    MSG(나중에 고칠 것);
    return 0;
}

신고
  1. Favicon of http://eslife.tistory.com BlogIcon eslife at 2010.01.14 23:28 신고 [edit/del]

    재호님 글 잘 봤습니다.
    저도 가끔씩 이용하는 방식인데.. 재호님처럼 매크로와 얍샵이 방법까지 활용할 생각은 못해 봤습니다. 좋은 방법 배우고 갑니다 ^^

    Reply
  2. Favicon of http://www.voiceportal.co.kr BlogIcon 김태정 at 2010.01.21 00:43 신고 [edit/del]

    오호~ 오랜만에 글?? ㅎㅎ

    Reply

submit
게임 개발자를 위한 C++ - 8점
서진택 지음/민프레스(민커뮤니케이션)

대학교 2학년 2학기 때, C언어에 어느 정도 익숙해지고 C++을 혼자서 조금씩 공부해 가던 때에, 친구의 추천으로 이 책을 읽었다.

당시 C++을 잘 모르던 내게 이 책은 너무나 버겨웠는데, 그럼에도 불구하고 이 정도 고급 주제를 다룬 국내 서적은 찾아 보기 힘들었기 때문에, 이해 될 때까지 연구실 책상에서 보고 또 보고 했던 기억이 난다.

그렇게 여러번 보다 보니 처음에는 전혀 모르겠던 내용들도 어렴풋이 이해하게 되고 나는 C++에 조금씩 익숙해져 갔다.

이 책은 내용은 괜찮았지만, 편집이 엉터리였다. 인쇄가 아마 -> 연산자가 모두 공백으로 나왔었언가? 그런 류의 심각한 결함이 있었다.

하지만 이 책에서 C++의 여러 문법들과, 오버로딩 오버라이딩, 상속 관계에서의 생성자와 소멸자의 호출 순서, 복사 생성자, this 포인터, 가상함수와 가상함수 테이블, 연산자 오버로딩등을 배울 수 있었다.

지금도 기억에 강하게 남아있는 부분은 복사 생성자인데, 이 책의 저자는 복사생성자를 참 중요하게 강조했던 것 같다.

복사 생성자란, 말 그대로 객체가 복사 될 때 호출되는 생성자이다.
CString s1(_T("string"));
CString s2( s1 ); // 또는 CString s2 = s1;


두번째 줄에서 s1을 s2에 복사하면서 복사 생성자가 호출 된다.
그냥 생성자가 아니라 CString::CString( const CString& ) 의 시그내쳐를 가진 복사 생성자 말이다.

하지만 코드를 이렇게 작성한다면,
CString s1(_T("string"));
CString s2;
s2 = s1;


위와 같은 경우에는 s2를 기본 생성자를 통해 객체를 생성한 뒤 operator=() 을 통해서 s1을 s2에 복사한다. 그러므로 위 코드에서는 복사 생성자가 호출 되지 않는다.

간단한 내용이지만, 생성자에서 멤버를 new하고 소멸자에서 그 멤버를 delete하는 경우에 복사 생성자를 정의해주지 않으면 복사 될 때 프로그램은 죽는다.
복사 생성자를 정의하지 않았을 때 컴파일러가 알아서 복사 생성자를 만들어 주지만 멤버에 포인터가 있다면 그 포인터 값을 그대로 복사하도록 코드를 생성한다.

이런 식으로 말이다.
class A
{
public:
    //somemethod();
private:
    int* pPointer;
};

A::A( const A& copy )
{
    this->pPointer = copy.pPointer;
}// 컴파일러는 이런식으로 복사생성자 코드를 만들어준다.


근데 A 클래스 생성자와 소멸자가 다음과 같았다고 하면,
A::A()
{
    this->pPointer = new int();
}

A::~A()
{
    delete this->pPointer;
}


이 클래스를 이런식으로 사용할 때,
{
    A obj1;
    A obj2 = obj1;
}


블락이 끝날 때에 프로그램은 죽는다.
- 사실 죽는다는 표현 보다는 정의되지 않은 동작을 수행한다. 라고 하는 것이 좀 더 정확하다. 하지만 일반적으로 거의 모든 경우에 뒤진다. :)

왜냐하면 obj1을 생성 할 때 불리는 기본 생성자에서 멤버 변수에 메모리를 동적 할당했고,
obj1을 obj2에 복사하면서는 복사생성자를 통해 obj2를 생성했는데, 이때는 pPointer에 새로운 메모리를 할당한 것이 아니라 obj1의 멤버 변수 pPointer의 주소만을 복사했기 때문이다.
블락이 끝나면서 소멸자가 호출 될 때는 각 객체의 소멸자가 모두 호출 되면서 pPointer를 해제하려고 한다.
하지만 2번째 delete할 때 이미 해제한 포인터를 다시 delete하려고 하므로 오류.

이럴 때는 복사 생성자를 컴파일러에 맡기지 말고 다음처럼 직접 작성해주어야 한다.
A::A( const A& copy )
{
    this->pPointer = new int( *copy.pPointer );
}


복사 될 때도 메모리를 동적으로 할당하고 복사본의 값을 가져오면서 deep copy가 되었다.

복사 생성자가 언제 호출되는지 아는 것도 중요하다.
첫번째 경우는 위 코드에서 설명했고,
두번째 경우는 함수 파라메터로 넘길 때이다.
void SomeMethod( A obj );

이런 함수가 있다면,
A obj;
SomeMethod( obj );

이렇게 호출 할때에 객체가 복사되면서 복사 생성자가 호출된다.

만일 함수가 void SomeMethod( A& obj ) 이렇게 참조를 받도록 정의되어 있다면, 이런 함수들은 물론 호출 할때 객체가 복사되지 않으므로 복사생성자도 호출되지 않는다.

마지막은 객체를 리턴할 때이다.
{
    A obj;
    // some codes...
    return obj;
}


이 때 역시 객체를 복사하므로 복사생성자가 호출 된다.

물론 C#이나 자바 등의 언어에서는 참조자만 사용되고 garbage collector 가 자원 해제 몫을 처리해 주기 때문에 이런 종류의 고민은 하지 않아도 된다.

신고
  1. 북이™ at 2008.05.11 23:53 신고 [edit/del]

    글고보니 저 책 랩실에 있는듯해요!!
    저도 한번 봐야겠는데요 ㅋㅋ
    열심히 할게요!!

    Reply
  2. 김재호 at 2008.05.12 12:37 신고 [edit/del]

    쿠쿠 그래.
    근데 넌 누구니? 재욱인가? 북이가 누구여

    Reply

submit
C++ Standard Library : 튜토리얼.레퍼런스 - 10점
니콜라이 M. 조슈티스 지음, 문정환 외 옮김/정보문화사

이 책을 읽기 위해선 먼저 C++, 템플릿 그리고 자료구조에 대한 이해가 필요하다.
자료구조는 리스트, 큐, 스택, 해쉬 테이블 정도를 직접 구현해봤거나 내부 동작을 잘 이해하고 있다면 충분하다.

STL 자체를 어떻게 사용하는가에 대한 예제 코드도 물론 필요 하겠지만,
STL이 얼마나 잘 정립된 개념을 가지고 있는지 이해하는 것도 중요할 것이다.

이 책에서는 위 2가지를 다 얻을 수 있다.

C++로 거의 모든 작업을 하고 있지만 나는 종종 C++에서 벗어나고 싶다.
널 포인터 검사를 꼬박꼬박 하는 것도 싫고, new delete 쌍 맞춰주는 것도
머리 아프다.

하지만 잘 작성된 C++ 클래스들은 나를 다른 언어로 도망치지 못하게 만든다.
STL의 모든 클래스들은 아름답다.

번역 또한 마음에 들며, STL을 처음 시작하는 사람들은 이 책으로 시작하면 좋을 것이다.( 스캇 마이어스의 Effective STL 이런거 고르지 마시고. )


신고
C++, stl,

submit