2014년 4월 15일 화요일

로그를 좀더 이쁘게 박아보자(g++ 기준)

로그를 작성할 때 그냥 막 적어도 상관은 없다만 이왕이면 해당 파일명과 소스라인 위치 함수명까지 알면 더욱 좋을것이다. 언제나 그렇듯이 긴말 말고 아래 매크로 또는 클래스들을 보자.
  • __FILE__ : 소스코드가 포함된 파일 이름을 반환
  • __LINE__ : 소스코드의 줄 번호를 반환
  • __func__ : 소스코드가 포함된 함수 이름을 반환
  • __PRETTY_FUNCTION__ : 소스코드가 포함된 클래스 이름과 함수 이름을 '이쁘게' 반환
  • typeid(T t).name() : 맹글링된 클래스 이름이나 타입 이름을 반환
다시 말하지만 위 매크로들은 g++ 기준이다. MSVCclang에서는 이와 같거나 다른 이름을 가지지만 같은 역할을 하는 매크로가 있을 것이다. 아래는 위 매크로를 이용한 간단한 예제이다.


  1 #include <iostream>
  2 #include <typeinfo>
  3
  4
  5 class CPrettyLog
  6 {
  7 public:
  8     void Print(void)
  9     {
 10         std::cout
 11             << "__PRETTY_FUNCTION__ = " << __PRETTY_FUNCTION__ << std::endl
 12             << "__func__ = " << __func__ << std::endl
 13             << "__LINE__ = " << __LINE__ << std::endl
 14             << "__FILE__ = " << __FILE__ << std::endl
 15             << "typeid(this).name() = " << typeid(this).name() << std::endl
 16             << std::endl;
 17     }
 18 };
 19
 20
 21 void Print(void)
 22 {
 23     std::cout
 24         << "__PRETTY_FUNCTION__ = " << __PRETTY_FUNCTION__ << std::endl
 25         << "__func__ = " << __func__ << std::endl
 26         << "__LINE__ = " << __LINE__ << std::endl
 27         << "__FILE__ = " << __FILE__ << std::endl
 28         << std::endl;
 29 }
 30
 31
 32 int main(int argc, char** argv)
 33 {
 34     CPrettyLog pl;
 35     pl.Print();
 36
 37     Print();
 38
 39     std::cout
 40         << "__PRETTY_FUNCTION__ = " << __PRETTY_FUNCTION__ << std::endl
 41         << "__func__ = " << __func__ << std::endl
 42         << "__LINE__ = " << __LINE__ << std::endl
 43         << "__FILE__ = " << __FILE__ << std::endl
 44         << std::endl;
 45
 46     return 0;
 47 }
 48

위 예제를 컴파일 후 실행하면 아래와 같은 결과를 얻을 수 있다.


__PRETTY_FUNCTION__ = void CPrettyLog::Print()
__func__ = Print
__LINE__ = 13
__FILE__ = main.cpp
typeid(this).name() = P10CPrettyLog

__PRETTY_FUNCTION__ = void Print()
__func__ = Print
__LINE__ = 26
__FILE__ = main.cpp

__PRETTY_FUNCTION__ = int main(int, char**)
__func__ = main
__LINE__ = 42
__FILE__ = main.cpp


2014년 4월 8일 화요일

리눅스의 디렉토리 구조와 역할

리눅스의 각 디렉토리 구조는 커널버전과 제품군에 따라 조금씩 다르지만 기본적인 맥락은 거의 비슷하다. 개인적으로 장문의 글로 장황하게 설명하는 걸 싫어하니 아래 트리와 주석을 보도록 하자.

~           
├── bin/    
├── lib/    
├── include/
├── src/    
├── etc/    
├── var/    
└── tmp/    

  • ~
    • 유저의 홈 디렉토리
    • 유저 디렉토리의 모든 시작지점
  • bin/
    • 실행 바이너리 및 쉘 스크립트들이 있음
    • binary file, .sh, .java
  • lib/
    • 라이브러리 파일들이 있음
    • .a, .so, .jar
  • include/
    • 헤더 및 전처리 파일들이 있음
    • 주로 lib/에서 필요한 헤더들이 있음
    • .h, .hh, .hpp, .py
  • src/
    • 작업중이거나 빌드 전의 소스코드들이 있음
  • etc/ 
    • 설정값이나 정보와 같이 고정적인 값들을 저장한 파일들이 있음
    • 주로 바이너리 이름 뒤에 확장자를 붙여 사용함
    • .conf, .nfo, .info
  • var/ 
    • 로그나 상태값과 같은 수시로 변하는 의미있는 정보들을 저장한 파일들이 있음
    • 주로 바이너리 이름을 가지는 디렉토리 내에 저장됨
    • .log, .stat, .queue, .pool
  • tmp/ 
    • 작업 중 임시로 사용되거나 수시로 생성/삭제되는 파일들이 있음
    • 주로 바이너리 이름을 가지는 디렉토리 내에 저장됨
    • .dummy, .buffer, 작업중인 파일 등

물론 모든 리눅스 시스템과 프로젝트들이 위 규칙을 따르는 건 아니지만 적어도 몇십년동안 많은 개발자와 관리자들에게 사랑을 받아온 구조에는 그만한 이유가 있다. 필자 또한 처음에는 이런저런 구조를 도입해보려 했었지만 결국 종착역은 위 구조였다. 그 이유는 아래와 같다.

  • 한 솔루션 내 여러 프로젝트 사이에서 일어나는 사이드 이펙트를 빠르고 직접적(?)으로 파악할 수 있음(이 얘기에 대해서는 추후 리눅스의 이상적인 솔루션(프로젝트) 구조에서 다루도록 하겠음)
  • 프로젝트의 유지보수 및 패치작업이 수월함
  • 관리자와 개발자 간 의사소통이 원활해짐

일단 이번 포스트는 이쯤에서 마무리 지으려고 한다. 쓰다보면 끝이 없을 것 같다.
이번 포스트의 핵심은 '몇십년동안 사랑을 받아온 구조에는 그만한 이유가 있다'



blogDevCrazyBird.Start();

다시 블로그를 운영하기로 마음 먹었다.
이유는 일반 SNS로는 다 표현하기 힘든 일기성 글이라던가 각종 정보성 기사들의 스크랩 및 개인적인 개발 공부를 위해서다. 앞으로 리눅스 및 C++(특히 C++11/14) 위주의 포스트가 주를 이룰 듯 하다. 매주 화/수/목 진행하는 아프리카 방송 다시보기도 업로드할 예정이다.
부디 이번에는 귀차니즘과 게임에 빠져서 유령 블로그가 되지는 않았으면 한다.