C/C++: __LINE__, __FILE__, __FUNCTION__ 의 활용

C/C++ 프로그램이 오류를 일으켰을 때, 오류의 원인을 파악하기 위해 필요한 정보들이 있다. 파일 이름, 함수 이름, 줄 번호 따위이다. 다행히, C/C++ 컴파일러는 컴파일러 수준에서 이러한 정보들을 제공하고 있다. 바로 __FILE__, __FUNCTION__, __LINE__ 따위가 그것이다. 이름에서 알 수 있듯이, __FILE__ 은 파일이름, __FUNCTION__ 은 함수 이름, __LINE__ 은 줄번호를 나타낸다.

여기에서 주의할 점은 __FILE__ 과 __LINE__ 은 문자열 상수 매크로이지만, __FUNCTION__ 은 컴파일러에 따라 또는 컴파일 모드(C 모드인지 C++ 모드인지)에 따라 문자열 상수 매크로일 수도 있고 변수일 수도 있다.

매크로인지 변수인지에 따라 무엇이 달라지는 것일까 ? 만일 매크로라면 다음과 같은 문법이 가능하다.

1
const char *str = "str is in " __FUNCTION__;


하지만 변수라면 컴파일러는 에러라며 불평할 것이다. 따라서 __FUNCTION__ 은 변수라고 가정하고 프로그래밍하는 것이 컴파일러의 특성에 의존하지 않는 가장 안전한 방법이다.

그리고 __FUNCTION__ 하위 호환성을 위해 제공되는 것이고, c99 을 지원하는 컴파일러에서는 __func__ 도 쓸 수 있다. 물론, __func__ 는 변수다.

이것들을 어떻게 사용할까? 프로그래머마다 다르겠지만, 내 경우에는 진입/탈출 로그를 만들기 위해 많이 사용한다. 다음은 실제 사용예이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

#define LOG_ENTRY fprintf( stderr, "%s(line %d): %s entry\n", \
                                   __FILE__, __LINE__, __FUNCTION__ )

#define LOG_EXIT  fprintf( stderr, "%s(line %d): %s exit\n", \
                                   __FILE__, __LINE__, __FUNCTION__ )

int main( void )
{
    LOG_ENTRY;

    printf("Hello, word\n");

    LOG_EXIT;

    return 0;
}



실행 결과는 다음과 같다.

1
2
3
logtest.c(line 10): main entry
Hello, word
logtest.c(line 14): main exit


하지만, 이 경우에는 return 전에 반드시 LOG_EXIT 를 일일이 해줘야 하는 단점이 있다. 물론 LOG_EXIT 에 return 기능을 추가해 주면 어느 정도 해결할 수는 있지만, 번거로운 것은 어쩔 수 없다.

반면에 C++ 에서는 보다 편리하게 쓸 수 있다. C++ 에서 클래스는 스코프의 진입/탈출에 맞춰 생성/소멸이 저절로 이루어지기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <string>

class LogEntryExit
{
public:
    LogEntryExit(const std::string& file, const std::string& func)
        : _file(file), _func(func)
    {
        std::cerr << _file << ": " << _func << " entry\n";
    }

    ~LogEntryExit()
    {
        std::cerr << _file << ": " << _func << " exit\n";
    }

private:
    std::string _file;
    std::string _func;
};

#define LOG_ENTRY_EXIT LogEntryExit logEntryExit(__FILE__, __FUNCTION__)

int main( void )
{
    LOG_ENTRY_EXIT;

    printf("Hello, world\n");

    return 0;
}



실행 결과는 다음과 같다.

1
2
3
logtest.cpp: main entry
Hello, world
logtest.cpp: main exit


이렇게 하면 언제 어디서 return 을 만나더라도 함수가 끝났다는 메세지를 볼 수 있게 된다. 물론 줄번호를 표시하지는 못한다는 단점이 있지만. 해결할 수 있는 좋은 아이디어가 있는 분들은 꼭 알려주시기를...

댓글

이 블로그의 인기 게시물

토렌트: < 왕좌의 게임 > 시즌 1 ~ 시즌 8 완결편 마그넷

토렌트: NGC < 코스모스 > 우리말 더빙 전편(1편~13편) 마그넷

토렌트: < 스타워즈 > Ep.1 ~ Ep.6 마그넷