Graphics Programming
RAII 이용해서 간단하게 로그 남기기 본문
로그를 찍을 때 작업의 디테일한 정도나 호출 순서를 알아보기 쉽게 하려고 들여쓰기를 쓰는 경우가 있다.
단순한 상황으로 함수 A 안에서 함수 B를 호출할 때, 콘솔에 A를 쓰고 들여쓰기한 다음 B를 쓴다고 치자.
#include <iostream>
void A() {
std::cout << "A" << std::endl;
B();
// do something
}
void B() {
std::cout << "\tB" << std::endl;
// do something
}
타자를 줄이기 위해 매크로를 정의한다.
#include <iostream>
#define LOG(x) { std::cout << x << std::endl; }
void A() {
LOG("A")
B();
// do something
}
void B() {
LOG("\tB")
// do something
}
탭을 직접 찍을 필요는 없는 것 같다. 탭도 매크로로 만든다.
#include <iostream>
#include <string>
static int __tabCount = 0;
static std::string __tabString = "";
#define LOG(x) { std::cout << __tabString << x << std::endl; }
#define TAB() { __tabCount++; __tabString = std::string(__tabCount, '\t'); }
#define UNTAB() { __tabCount--; __tabString = std::string(__tabCount, '\t'); }
void A() {
LOG("A")
TAB()
B();
// do something
UNTAB()
}
void B() {
LOG("B")
// do something
}
함수 이름을 로그에 찍는 것이 목적이었다.
#include <iostream>
#include <string>
static int __tabCount = 0;
static std::string __tabString = "";
#define LOG(x) { std::cout << __tabString << x << std::endl; }
#define FUNCNAME() LOG(__FUNCTION__)
#define TAB() { __tabCount++; __tabString = std::string(__tabCount, '\t'); }
#define UNTAB() { __tabCount--; __tabString = std::string(__tabCount, '\t'); }
void A() {
FUNCNAME()
TAB()
B();
// do something
UNTAB()
}
void B() {
FUNCNAME()
// do something
}
그런데 이렇게 하면 항상 함수 첫머리에 FUNCNAME()과 TAB()을 쓰고 종료 직전에 UNTAB()을 써야 한다. 함수 중간에 return이라도 있으면 더 귀찮다. 탭이 꼬이지 않게 일일이 UNTAB()을 넣어야 한다.
#include <iostream>
#include <string>
static int __tabCount = 0;
static std::string __tabString = "";
#define LOG(x) { std::cout << __tabString << x << std::endl; }
#define FUNCNAME() LOG(__FUNCTION__)
#define TAB() { __tabCount++; __tabString = std::string(__tabCount, '\t'); }
#define UNTAB() { __tabCount--; __tabString = std::string(__tabCount, '\t'); }
void A() {
FUNCNAME()
TAB()
if(foo == bar) {
UNTAB()
return;
}
B();
if(baz == nullptr) {
UNTAB()
return;
}
UNTAB()
}
void B() {
FUNCNAME()
// do something
}
이럴 때는 생성자에 FUNCNAME()과 TAB()이 있고 소멸자에 UNTAB()이 있는 구조체를 하나 정의하면 일이 편해진다.
#include <iostream>
#include <string>
static int __tabCount = 0;
static std::string __tabString = "";
#define LOG(x) { std::cout << __tabString << x << std::endl; }
#define FUNCNAME() LogFuncName __logFuncName(__FUNCTION__);
#define TAB() { __tabCount++; __tabString = std::string(__tabCount, '\t'); }
#define UNTAB() { __tabCount--; __tabString = std::string(__tabCount, '\t'); }
struct LogFuncName {
LogFuncName(const char* fname) {
LOG(fname)
TAB()
}
~LogFuncName() { UNTAB() }
};
void A() {
FUNCNAME()
if(foo == bar) {
return;
}
B();
if(baz == nullptr) {
return;
}
}
void B() {
FUNCNAME()
// do something
}
주의점
- LogFuncName 안에서 __FUNCTION__을 쓰면 "LogFuncName"이 찍히기 때문에 함수 이름 문자열을 인자로 받는다.
- FUNCNAME()의 정의가 { LogFuncName __logFucName(__FUNCTION__); } 이 아니라 LogFuncName __logFucName(__FUNCTION__); 이다. 중괄호로 감싸면 구조체 생성 후 바로 파괴되기 때문에 __tabCount가 항상 0에 머무른다. 그러면 로그에 탭이 찍히지 않는다.
예시: 함수 시작 부분마다 FUNCNAME()을 써서 호출 깊이와 순서를 확인