본문으로 바로가기

ASSERT_VALID, AssertValid, Dump

category 개발, IT 2008. 6. 18. 09:27


ASSERT_VALID, AssertValid, Dump에 대해서 알아보자. 별로 쓸 일이 있을까 싶지만, 원도우 프로그램을 하면서 무심코 가장 많이 접하는 함수 중에 하나였을 것이다.(이 함수를 처음 본다는 사람은 그냥 무심결에 넘어가서 그렇지, 그렇지 않다면 좀 문제가 있을 것 같다.)

예를 들어 원도우 프로그램을 하나 자동으로 생성하면, 일반적으로 CXXXApp, CXXXView, CXXXDoc, CMainFrame 등의 class가 자동으로 만들어진다. 이때 CXXXApp를 제외한 아무 class나(여기서는 CXXXView)를 열어보면 CXXXView.h에는 

#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

와 같이 정의되어 있고, CXXXView.cpp에는 위 함수가 구현되어 있고, 또한 OnDraw()에 가보면 ASSERT_VALID()를 호출하는 것이 보일 것이다.

void CXXXView::OnDraw(CDC* pDC)
{
    CXXXDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // TODO: add draw code for native data here
}

이번 시간에는 다시 말하지만, 자동으로 생성되는 이 함수들이 뭔지 정도만 알고 넘어가자.

1.ASSERT_VALID(), AssertValid()

- ASSERT_VALID(p);처럼 사용한다. 이때 p는 반드시 본인 또는 parent class가 CObject 타입이어야 한다. 물론 위의 예제처럼 ASSERT_VALID(pDoc);의 pDoc가 CDocument class를 상속받고, CDocument class의 최상위 parent class가 CObject class
라는 것을 굳이 설명할 필요는 없겠지만, 예를 하나 들어보면,
char* p = NULL;
ASSERT_VALID(p);
로 작성하여 컴파일을 해보면, error C2664: 'AfxAssertValidObject' : cannot convert parameter 1 from 'char *' to 'const class CObject *' Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast의 에러가 나타나는 것을 볼 수 있을 것이다.

- 이제 ASSERT_VALID(p)가 어떻게 동작을 하는지 살펴보자.
이 함수는 내부적으로
ASSERT(p);
p->AssertValid();
위와 같이 동작한다. 이전 시간에 설명한 것과 같이 p가 NULL이라면, ASSERT(p)에서 런타임에 에러박스를 보일 것이고, 그렇지 않다면 p->AssertValid()에 의해 AssertValid()가 호출될 것이다. 여기서, class의 polymorphism에 의해 p class의 타입에 맞는 AssertValid()가 호출될 것이다.(무슨 말인지 모르겠다면 class 기초 책에서 polymorphism 참조)

- 정리를 해보면, AssertValid() 함수에 디버깅 할 내용을 작성해두고, ASSERT_VALID(p)를 호출하는 식으로 이용을 하면 되겠다. 마지막 예제 참조.

2.Dump(CDumpContext& dc)

- p->Dump(afxDump);와 같이 사용한다. 이때 afxDump는 VC++에 기본적으로 있는 Global 변수의 CDumpContext 타입의 객체이다. 기본적으로는 TRACE()와 같이 디버깅 때의 Output의 Debug Tab에 결과를 보여주는 역할을 하는 변수이고, 사용자의 정의에 따라 파일로 결과를 적을 수도 있다.

3.마지막으로 예제를 하나 살펴보자.


사용자 삽입 이미지

- 첫번째 빨간줄: ASSERT_VALID(a);를 호출하기 위해서 "public CObject"
- 두번째, 세번째 빨간줄: override, polymorphism을 위해 CObject class의 함수를 재정의
- 네번째 빨간줄: AssertValid()에는 디버깅을 위한 구문을 알아서 작성. 보통은 포인트의 NULL 여부 체크에 이용
- 다섯번째 빨간줄: 디버깅 중에 Output의 Debug Tab에 TRACE와 같이 결과가 나타남. 보통은 class의 member variables들을 표시하여 디버깅하는데 이용.

사용자 삽입 이미지



- 여섯번째 빨간줄: ASSERT_VALID(a);는 내부적으로 ASSERT(a), a->AssertValid() 호출
- 일곱번째 빨간줄: 일단 모든 것은 Debug 모드에서 동작을 하지만, 이 줄을 빼고, Release 모드에서 컴파일하면 "error C2065: 'afxDump' : undeclared identifier"의 에러 발생. 즉, Debug 모드에서만 afxDump 변수가 선언되어 있음.
- 여덟번째 빨간줄: Dump(CDumpContext& dc)를 호출함.

참고로 Dump의 값을 파일에다가 표시하고 싶으면,

BOOL CXXXApp::InitInstance()
{
#ifdef _DEBUG
CFile* pFile;
ASSERT(afxDump.m_pFile == NULL);
pFile = new CFile(_T("dump.bug"), CFile::modeCreate | CFile::modeNoTruncate | CFile::typeText | CFile::modeWrite);
afxDump.m_pFile = pFile;
afxDump.m_pFile->SeekToEnd();
#endif
...
}

int CXXXApp::ExitInstance()
{
#ifdef _DEBUG
if(afxDump.m_pFile != NULL)
{
afxDump.flush();
afxDump.m_pFile->Close();
delete afxDump.m_pFile;
afxDump.m_pFile = NULL;
}
#endif
return CWinApp::ExitInstance();
}
와 같이 하면 되겠다. 그러면 Output의 Debug Tab이 아닌 파일에 적힐 것이다. 즉, 프로그램 시작시 afxDump의 기본 출력을 파일에다가 연결하고, 프로그램 종료시 파일을 닫는 것이다. 무슨 말인지는 이해가 되리라 생각을 한다.

이제 ASSERT_VALID, AssertValid, Dump가 무엇인지 충분히 이해가 되었다고 생각한다. 사용할 필요는 없다고 생각을 하지만, 자주 접하는 저 함수가 뭔지 알아두는 것도 나쁘지 않을 것이다. 이제 디버깅 관련으로 한 1/3정도 진행한 것 같다. 많이 지루하겠지만 조금씩 따라오면 조금은 도움이 되리라 생각한다.


좋은정보가 되셨다면 아래 한번 클릭해주세요^^




댓글을 달아 주세요

  1. BlogIcon greenfrog 2009.01.09 10:54

    좋은 글 감사합니다. 공부에 많은 도움이 되었네요 ^^

  2. gowe3 2018.05.26 10:52

    좋네요, 아직 초보라 ,어려운지도 모르겠네요.