글 시작 전에 이 글은 GCC, Visual Studio 모두 사용 할 수 있는 방법임을 밝힌다.

    단, 본문은 Visual Studio를 기준으로 설명한다. GCC에서도 그대로 쓰거나 조금 변경하면 쉽게 사용이 가능하다.


    예전 학교나 대학원에서 GCC를 주로 쓸 때는 IDE에 대한 의존도가 전혀 없었다. 사실 IDE라고 하기도 적절치 않다. GCC와 CSCOPE, VIM 요 세 가지로 모든 코딩을 다 했으니 말이다.

    하지만 회사를 다니고 Visual Studio를 주로 쓰다 보니(업무 상 쓸 뿐 개발 환경에 대한 장단 비교는 아니다.) IDE에 대한 의존도가 점점 올라가게 되었다.


    여러 장단점이 존재 하겠지만 IDE 편리함이라는 장점이 의외로 크게 다가온다.

    IDE에서 제공해주는 북마크와 같은 기능이 이에 해당 한다. 


    하지만 이러한 편리함에 적응이 될 수록 더 입맛에 맞는 기능을 찾게 되고 종종 IDE에 실망을 하게 된다.


    최근 코딩하면서 이슈 등을 관리 하기 위하여 Visual Studio의 북마크 기능과 작업 목록 기능을 사용 한 적이 있다.


    Visual Studio의 북마크 기능


    Visual Studio의 작업 목록 기능


    사용하다 보니 두 기능 모두 조금씩 불편한 점들이 존재 했다.


    북마크의 경우 파일의 위치나 북마크의 위치 등을 북마크 창을 통하여 보여 주기도 하고 소스 코드 왼편에 표시를 해 주어 상당히 만족 스러웠다.

    하지만 북마크를 등록 할 때 어떠한 북마크인지 메모를 추가하기는 상당히 불편했다.


    그래서 북마크 대신 작업 목록을 한동안 사용햇었는데 작업 목록의 경우 치명적인 단점이 현재 편집 중인 파일의 작업 목록만 보인다는 사실이였다.


    두 기능 모두 입맛에 맞지 않아 실망 하던 터에 Visual Studio 컴파일 도중 나타나는 출력 창에 이슈 메세지를 출력 하면 어떨까 하고 찾아 보게 되었다.

    전처리기 기능 중 컴파일 시 메시지를 출력 할 수 있는 방법이 있었고 Visual Studio에서는 해당 메시지를 출력 창에 표시해 주었다.

    또한 Visual Studio의 해당 기능은 컴파일 에러나 경고 처럼 더블클릭을 통해 해당 소스 위치로 바로 이동도 가능하다는 사실도 알게 되었다.


    먼저 컴파일 시 출력창에 메시지를 표시하기 위해서는 #pragma 전처리기 기능 중 message를 활용 해야 한다.

    #pragma message의 사용 방법은 아래와 같다.

    #pragma message ("#pragma message example")

    #pragma message 키워드와 함께 출력 할 메시지를 전달해 줌으로써 간단히 출력 창에 메시지를 표시 할 수 있다.

    #pragma message를 사용하여 소스 코드에 추가 한 후 컴파일 한다면 아래와 같은 모습을 볼 수 있을 것이다.


    #pragma messag를 이용한 컴파일 메시지 출력


    위 사진을 보면 알 수 있겠지만 파일에 대한 정보나 라인에 대한 정보가 없을 뿐더라 실제로 더블클릭을 해도 해당 소스 코드로 이동을 하지도 않는다.


    이제부터 위 기능들을 추가 해 보도록 하겠다.


    먼저 아래와 같은 매크로를 추가 하도록 하자.

    #ifndef REMINDER
    #define STRINGIZE(l)	#l
    #define REMINDER(l, m)	__FILE__ "(" STRINGIZE(l) "): "m
    #define TESTCODE		REMINDER(__LINE__, "TEST CODE WAS COMPILED, ")
    #define TODO			REMINDER(__LINE__, "TODO, ")
    #define XXX				REMINDER(__LINE__, "!!!!!!!!!! XXX !!!!!!!!!! ")
    #endif
    

    위 매크로가 이해가 되지 않더라도 일단은 넘어가길 바란다. 본 글 마지막 부분에서 다시 설명을 해 주도록 하겠다. 


    위 매크로를 사용 할 경우 아래와 같은 형태로 #pragma message를 사용 할 수 있다.

    #pragma message (TESTCODE "#pragma message example")
    #pragma message (TODO "#pragma message example")
    #pragma message (XXX "#pragma message example")
    

    위와 같이 소스 코드를 수정 한 후 컴파일 한다면 출력 창에는 아래와 같은 메세지가 출력 된다.


    #pragma messag 메시지에 소스 코드 정보 및 라인 정보를 추가하여 출력


    매크로를 추가 하여 #pragma message와 함께 씀으로써 파일에 대한 정보나 라인에 대한 정보가 쉽게 추가 되었다. 

    이 글을 따라하고 있는 독자 라면 출력 된 메세지 부분을 더블클릭해보라. 

    에러나 경고를 더블클릭 했을 때 처럼 소스 코드로 바로 이동 하는 것을 알 수 있을 것이다.

    굉장히 쉽게 모든 기능이 구현 되었다.


    __LINE__, __FILE__ 은 컴파일 시 전처리기가 치환을 해주는 값이고, 소스 코드로 바로 이동 하는 기능은 해당 정보가 있을 때 Visual Studio가 적절히 처리를 해주는 것으로 보인다.


    전처리기가 치환을 해 주는 값에는 비단 라인 정보 나 파일 정보 외에 함수 이름 등도 얻을 수 있다. 

    해당 정보가 필요한 독자라면 위 매크로에 추가하여 쉽게 사용이 가능 할 것으로 보인다.


    해당 기능을 적용 하여 코딩을 한다면 컴파일 할 때마다 아래와 같이 관심 있는 정보를 컴파일 할 때마다 얻을 수 있으므로 

    테스트 코드의 빌드 여부, 리마인드 해야 할 정보를 항상 예의주시 할 수 있어 프로그래머의 실수를 막아 준다.


    본 글에서는 매크로로 TESTCODE, TODO, XXX 3가지 매크로를 적용 했다.

    TESTCODE의 경우 필자가 필요에 의해 추가한 매크로 이름 이지만, TODO나 XXX 등은 두루 쓰이는 이름 이니 알아 두면 좋을 듯 하다.

    (TODO, XXX외에 FIXME, HACK도 많이 쓰이는데 쓰임세는 다른 글들을 참고 하기 바란다.)


    간단한 동작 원리를 설명 하면 전처리기와 컴파일러가 이미 정의 해둔 매크로들, 그리고 문자열의 특징을 이용하여 해당 기능이 구현 된다.

    아래 TODO를 사용하여 설명을 하면 다음과 같다.

    #pragma message (TODO "#pragma message example")
    

    전처리기에 의해 먼저 TODO가 치환이 되는데 치환 결과는 아래와 같다.

    #pragma message (REMINDER(__LINE__, "TODO, ") "#pragma message example")
    

    순차적으로 전처리기에 의해 남아있는 REMINDER 매크로, __LINE__ 매크로(__LINE__은 컴파일러에 의해 정의되는 매크로이다)가 치환 되면 아래와 같다.

    (해당 #pragma는 소스 코드 상 31번째 라인에 위치 한 것으로 하겠다.)

    #pragma message (__FILE__ "(" STRINGIZE(31) "): ""TODO, " "#pragma message example")

    마지막으로 __FILE__ 매크로, STRINGIZE 매크로가 치환이 되게 되면 아래와 같은 결과물이 나온다.

    #pragma message ("example.cpp" "(" "31" "): ""TODO, " "#pragma message example")
    


    STRINGIZE(31)이 "31" 로 치환 되는 것은 전처리기에서 매크로가 치환 될 때

    #define STRINGIZE(l) #l 의 동작 때문이다.

    전처리기에서 #l 은 l을 쌍따옴표""로 감싸게 되어 l의 내용이 문자열로 치환 되는데 l에 31이 들어왔으므로 "31"로 치환 되게 된다.

    따라서 위와 같은 결과물이 생성 되고 컴파일러는 여러 인접한 문자열을 하나의 문자열로 취급 하기 때문에 아래와 같은 하나의 문자열로 되어 최종 결과물로 인식 된다.

    #pragma message ("example.cpp(31): TODO, #pragma message example")

    위 결과물이 실제로 컴파일 되므로 출력 창에 파일이름, 줄번호와 함께 메시지가 표시 된다.


    끝으로 취향에 따라서 매크로를 조금 더 수정 하면 TODO나 XXX와 같은 매크로에 #pragma message 부분도 넣어 버려 아래와 같이 사용하게끔 수정 할 수도 있긴 하다.

    TODO("#pragma message example")
    XXX("#pragma message example")
    

    하지만 필자가 선호하는 스타일은 아니다.

    #pragma message 부분까지 넣는 다면 사용할 때 편할 지는 모르겠으나 해당 부분을 봤을 때 전처리가 처리하는 부분인지 컴파일러가 처리하여 런타임에 동작하는 부분인지 구분이 가지 않는다.

    즉, 처음 코드를 접하는 프로그래머 해당 부분이 출력시 메시지를 찍는 부분인지 런타임에 로그를 남기는 부분인지 모호 할 수 있다는 것이다.

    필자는 이런 모호함을 최대한 배제 하는 스타일이므로 최소한의 #pragma message 부분은 남겨 두어 이 부분은 전처리가 처리 하는 부분이고, 컴파일 시 출력 되는 부분이라는 정보를 남겨 처음 접하는 프로그래머도 쉽게 이해 할 수 있는 방법을 추천한다.

    Posted by 이거니거니료니