Logger 개발기 - 전처리기와 컴파일러

2023. 6. 11. 01:25뚝딱뚝딱 만들기 Devlog/게임엔진 (Ramensoup)

Ramensoup(라면스프) 프로젝트에 대해..

 

오늘은 제 Logger 클래스에 대해 소개드리고자합니다.

 

우선 Logger 클래스에서 해야할 일은 아래와 같습니다.

1. 콘솔에 원하는 형태의 문자열을 출력할 수 있어야한다.

2. Release 모드에서는 로깅하지 말아야한다. if 문으로 막는 것이 아닌, 전처리기를 통해 함수 호출도 일어나지 않게 해야한다.

 

Hazel 에서는 spdlog라는 로깅 라이브러리를 활용했고, HZ_INFO 전처리기를 통해 간단하게 로깅을 구현했습니다.

spdlog : https://github.com/gabime/spdlog

 

GitHub - gabime/spdlog: Fast C++ logging library.

Fast C++ logging library. Contribute to gabime/spdlog development by creating an account on GitHub.

github.com

 

똑같이 가도 되지만.. 저는 괜히 매크로 함수로 로깅을 하는게 불만이었습니다.

Logger::Log(~~~) 형식으로 쓰고싶었습니다.

그래서 Log 함수 내부에 ifdef를 활용해 빈 함수로 만들고, 컴파일러가 이 빈 함수를 inline 해준다면, 컴파일러 최적화에서 호출 자체를 없애주는 일을 해주지 않을까..? 에서 시작한 제 Logger::Log 함수는 아래와 같습니다.

 

	class Logger
	{
	public:
		template<typename... Args>
		using FormattedString = spdlog::format_string_t<Args...>;
	public:
		static void Init();

		template<typename... Args>
		inline static void Log(FormattedString<Args...> format, Args &&... args)
		{
#ifdef RS_DEBUG
			s_Logger->trace(format, std::forward<Args>(args)...);
#endif
		}
   	 .
   	 .
   	 .
	}

여기서 궁금한 점은 "저 함수가 비어있다면 inline 되면서 함수 호출이 사라질 수 있을까?" 였습니다.

간단히 검색해봤을 땐 가변 인자 함수는 inline이 안된다고 해서, 테스트 프로젝트에서 직접 테스트를 해봤습니다.

 

릴리즈 모드에서 실행해 봤을 때, std::cout을 실행하는 10번줄 처럼 머신코드가 나와야하는데 Logger::Log를 실행하는 8번줄에는 아무런 머신코드가 생성되지 않았습니다!

 

이렇게 해서, 제가 원하는 인터페이스의 Logger를 만들었습니다!

이 방법의 단점이라고 한다면 Logger 클래스와 똑같은 CoreLogger 클래스가 있다는 점 (코드 중복)입니다.

이 코드 중복을 없애려고 tag 문자열을 템플릿 인자로 받는 Logger<tag>를 만드려했는데, 문자열이 컴파일타임 상수가 아니라섯 템플릿 인자로 사용할 수 없었고, 그걸 가능하게 하는 방법이 너무 복잡해, 그냥 포기했습니다. 어차피 Logger는 2개만 있으면 되니까요!

 

어쩌면 매크로함수로 로깅을 구현하는게 옳은 일일지도 모릅니다.

이런 방식으로 code stripping 하는 것은 컴파일러에 좀 의존적이라는 생각이 들어서입니다.

또 Assertion이나 Logging 등 디버깅 관련 함수들만 매크로로 만들면, 그것대로 의미가 있는 것 같습니다.

이건 나중에 이런 생각이 강하게 들면.. 수정해보는거로 해볼게요

반응형