C/C++: __LINE__, __FILE__, __FUNCTION__ 의 활용
C/C++ 프로그램이 오류를 일으켰을 때, 오류의 원인을 파악하기 위해 필요한 정보들이 있다. 파일 이름, 함수 이름, 줄 번호 따위이다. 다행히, C/C++ 컴파일러는 컴파일러 수준에서 이러한 정보들을 제공하고 있다. 바로 __FILE__, __FUNCTION__, __LINE__ 따위가 그것이다. 이름에서 알 수 있듯이, __FILE__ 은 파일이름, __FUNCTION__ 은 함수 이름, __LINE__ 은 줄번호를 나타낸다.
여기에서 주의할 점은 __FILE__ 과 __LINE__ 은 문자열 상수 매크로이지만, __FUNCTION__ 은 컴파일러에 따라 또는 컴파일 모드(C 모드인지 C++ 모드인지)에 따라 문자열 상수 매크로일 수도 있고 변수일 수도 있다.
매크로인지 변수인지에 따라 무엇이 달라지는 것일까 ? 만일 매크로라면 다음과 같은 문법이 가능하다.
하지만 변수라면 컴파일러는 에러라며 불평할 것이다. 따라서 __FUNCTION__ 은 변수라고 가정하고 프로그래밍하는 것이 컴파일러의 특성에 의존하지 않는 가장 안전한 방법이다.
그리고 __FUNCTION__ 하위 호환성을 위해 제공되는 것이고, c99 을 지원하는 컴파일러에서는 __func__ 도 쓸 수 있다. 물론, __func__ 는 변수다.
이것들을 어떻게 사용할까? 프로그래머마다 다르겠지만, 내 경우에는 진입/탈출 로그를 만들기 위해 많이 사용한다. 다음은 실제 사용예이다.
실행 결과는 다음과 같다.
하지만, 이 경우에는 return 전에 반드시 LOG_EXIT 를 일일이 해줘야 하는 단점이 있다. 물론 LOG_EXIT 에 return 기능을 추가해 주면 어느 정도 해결할 수는 있지만, 번거로운 것은 어쩔 수 없다.
반면에 C++ 에서는 보다 편리하게 쓸 수 있다. C++ 에서 클래스는 스코프의 진입/탈출에 맞춰 생성/소멸이 저절로 이루어지기 때문이다.
실행 결과는 다음과 같다.
이렇게 하면 언제 어디서 return 을 만나더라도 함수가 끝났다는 메세지를 볼 수 있게 된다. 물론 줄번호를 표시하지는 못한다는 단점이 있지만. 해결할 수 있는 좋은 아이디어가 있는 분들은 꼭 알려주시기를...
여기에서 주의할 점은 __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 을 만나더라도 함수가 끝났다는 메세지를 볼 수 있게 된다. 물론 줄번호를 표시하지는 못한다는 단점이 있지만. 해결할 수 있는 좋은 아이디어가 있는 분들은 꼭 알려주시기를...
댓글
댓글 쓰기