boost::program_options를 사용한 UnitTest++ 커맨드라인 인터페이스

어제 포스팅에서 불편하단 소릴 하긴했지만, 작년 한 해 동안 작성한 테스트 케이스는 전부 이 녀석으로 작성했고, 지금도 잘 돌아가고 있다.  수 시간 전에 빌드서버에서 실행된 애들도 있을듯…

다음과 같은 인자를 받아서 이를 해석하는데, 방식은 *nix의 getopt와 비슷하다. 즉, 

  • –help 에 도움말이 나오고
  • –long-option or -s 처럼 긴 옵션과 짧은 옵션이 같은 의미를 같게 짝 지어져 있고,
  • 대부분의 UNIX 명령어 같은 느낌으로 돈다

 

여튼 내가 만든 녀석은,

  • –suite 혹은 -s 로 실행할 테스트 스윗을 받고 (없으면 몽땅)
  • –testcase 혹은 -t 로 실행할 테스트 케이스 이름을 받고 (역시 없으면 몽땅)
  • –output 혹은 -o 로 테스트 리포트를 받는다. 이게 없으면 콘솔로 있으면 파일로. 그리고 파일 확장자가 .xml 이면 XML 형태로, 아니면 다시 콘솔로(…) 출력한다.

같이 동작한다. 물론 –help도 있다(…).

 

구현 코드는 boost::program_options를 써서 좀 뻔하다.

po::options_description desc("Allowed options" );
po::positional_options_description p;
p.add("suite", -1);

desc.add_options()
    ("help", "produce help message")
    ("output,o",   po::wvalue<wstring>(&out),      "test-report output path")
    ("suite,s",        po::wvalue<wstring>(),          "test-suite to run")
    ("testcase,t", po::wvalue<wstring>(),          "test-case to run")
    ;

po::variables_map vm;
po::store(
    po::wcommand_line_parser( argc, argv ).options( desc ).positional(p).run(),
    vm
);

po::notify(vm);

이 간단한 코드가 옵션을 정하고 / 이를 해석하는 코드가 된다.[1]

p.add( “suite”, -1 )은 기본 옵션 — 아무런 옵션 이름을 안주고 인자를 주면 그거에 대한 인자를 준걸로 받아들임 — 을 위해 사용한 것이니 신경쓰지 않아도 된다.

전체 코드는 여기에 링크 해 둔다. 이런 형태로  상당히 불편하긴 하지만, UnitTest++ 로 작성된 테스트 케이스들을 실행하는 커맨드라인 프로그램이 만들어진다. 뭐 이런 식으로 *nix 명령어/유틸리티스러운 커맨드라인 프로그램을 작성할 때 써먹어도 된다(사실 이 용도로 매우 잘 써먹고있긴하다).

  1. 덤으로 UTF-16 인코딩된 unicode 기반의 프로그램이라서 wstring을 쓴다 []

잡담: 2008년 4월 5일

boost 1.35 released

C++ 라이브러리의 정수라고 생각하는 boost의 새 버젼이 지난 3월 29일에 릴리즈되었다. Asynchronous IO 팩키지를 비롯하여 링버퍼, 함수타입, 퓨젼, 이미지 처리, TR1에 추가된 수학함수, 프로세스간 통신 등 여러가지가 추가되었다. Thread 팩키지의 Read/Write lock 문제가 해결되었는지 확인해봐야겠다.[1]

Apple의 액세서리 정책 맘에 안드네

프로그래밍을 하기엔 맥북의 화면 — 해상도 1280 by 800 — 은 너무 좁다. 회사에서 20″ 1440 * 1050이 되는 녀석이 너무 좁다고 17″ 하나 더 붙여쓰고, 그것도 부족해서 17″(17″는 모두 1280 * 1024) 하나 더 쓰고 있는 사람한테는 너무나도 좁다 -_-;;

그래서 DVI 케이블을 사볼까하고 a#에 갔는데 품절이라 한다. 그래서 Apple Store (온라인)을 뒤지는데, DVI 케이블이 DVI-D만 되고 D-SUB 호환성이 없구나 -_-a 가격은 개당 2.5만 (DVI, D-SUB 각각).

프로젝터나 KVM 스윗치 없는 장소 – 집이라거나 – 를 생각하면 D-SUB, (DVI는 이미 다 찼다), 회사나 아예 전용 모니터가 있으면(과연) DVI가 나을텐데 -,-

일단 D-SUB만 하나 사는걸 고민 중

Mac의 패스 문제

*nix-like OS이긴한데 기본적인 경로가 너무 차이가 크다. 예를 들어 /home/username -> /Users/username로 바뀌어있고, /usr/include, /usr/lib 같은건 또 원래대로고. NetBSD 처음 쓸 때보다 더 삽질하는 기분 -_-;;

어제 오늘 이런 저런 라이브러리[2] 를 설치하는데 적당한 경로를 고민하느라 삽질 좀 했다 — 최종적으론 포기하고 그냥 /Users/rein/include, lib을 만들어서 거기 죄다 몰아넣고 eclipse에 해당 경로를 추가하는걸로…

게다가 Xcode는 뭔가 carbon — native C/C++ 라이브러리 — 의 x64 지원이 없는 듯 하다. 이건 싸우잔건가 -_-;;

  1. 이걸 확인 못해서 SW잡담이 아님 []
  2. 다음 주에 있을 UPnL 워크샵에서 implicit concurrent programming을 주제로 할 생각이라 이것저것 준비해둘 라이브러리가 많다. 전번 발표 때 Python/VS/DX가 모두 안깔린 놋북에서 프리젠테이션하느라 삽질한걸 생각하면 이번엔 앗싸리 준비를 다 해두고 가려고 한다. 이번엔 콘솔 프로그램만으로 일단 될테니. []

지정문답: "boost 라이브러리"

고어핀드의 망상천국에서 넘겨받은 지정문답 : boost library

이 블로그에는 처음 넘어온 바톤을 처리합니다[…] (사실 전에 lazhu가 넘겼었지만 차마 못하고 넘어간게 있지만 …)

■ 바톤을 받는 5명, 절대로 5명! (지정과 함께)

 

이 마이너 블로그에 자주 찾아오는 사람들이라면 알 확률이 높겠지만(모르신다면 + 프로그래머라면 지금부터라도 늦지 않았습니다!), boost library는 C++ 프로그래머들에겐 꽤나 잘 알려진 C++ 라이브러리이다. 제공하는 기능도 많고, 사용하는 사람도 많아서 STL 처럼 편안하면서도 빠르고, 잘 테스트되서 믿을 수 있는 기능들을 쓸 수 있게 해준다. 덤으로 현존하는 주요 플랫폼(OS/CPU/컴파일러 묶음들)을 거의 지원하고 있다. (좋은 점은 거의다 가진 듯)

여튼 이 녀석을 주제로 지정문답 출발.

■ 최근 생각하는 “boost library”

최근 생각하는 정도가 아니라 매일 맞대는(…) 도구이기 때문에 과장 좀 섞어서 말하면,

“믿을 건 이놈뿐. 어서 표준에 들어가라!”

편하긴 무지 편한데, 완전히 표준에 들어간게 아니라서 (물론 일부가 tr1에 포함되었음) 일일이 소스 트리에 포함시키고 하는게 무지 귀찮아서, 좀 편하게 썼으면 하는 소망.

■ “boost 라이브러리” 에 감동

…문인이 시를 읽고 감동하는 것처럼 프로그래머도 코드를 보고 감동할 때가 있습니다 ;)  코드를 보고 감동했다기보단 boost 라이브러리의 기술문서(특히나 tr1에 넣기위한 제안서들)를 보면 C++ 전반을 관통하는 “철학”을 볼 수 있어서 좋습니다. 공학자답게

왜 필요한가

다른 대안은 있는가

그렇지만 현재 이 대안을 택하는 이유는 어떤가

를 명확히 설명해주는게 무척 감동적이었습니다(특히나 shared_ptr<T>의 기술문서가 :) )

■ 직감적 “boost 라이브러리”

프로그래머가 라이브러리에 직감을 느끼면 좀 위험하겠지만(…), 약간만 추론하면(+그리고 C++의 철학들을 조금 더하면) 직감적으로 이것저것 할 수 있는게 – 누군가의 표현을 빌자면 C++의 자연스러운 확장 – STL을 처음만졌을 때 이후로 가장 손에 감기는 무언가가 아닌가 합니다.

■ 좋아하는 “boost 라이브러리”

…뭔가 질문이 이상하지만[…]. boost 라이브러리 중에 프로그래머生을 편하게 해주는 – 그래서 좋아하는 – 것들을 꼽자면,

  • boost::shared_ptr<T> – 현재 가장 믿을 수 있는 (특히 멀티스레딩에서) 스마트 포인터 라이브러리
  • boost::bind, boost::lambda – C++에는 없는 익명함수를 최대한 따라잡게 해주는 녀석. STL 알고리즘에서 아쉬웠던 점을 마구마구 채워주는 라이브러리
  • boost::function, boost::mem_fn 등 함수 래퍼들 – 속도저하가 (거의) 없는 추상화의 진수를 보여주는, 그러면서도 기존 라이브러리와 조화롭고 + 인생을 편하게 해주는 라이브러리. 구현도 재밌으니 심심할 때 읽어보면 :p

정도가 아닐까 합니다. boost meta programming library도 재밌지만 패스[…]

■ 세계에 “boost 라이브러리” 가 없다면?

야근에 치이며, 속도는 안나오는 프로그램을 만들고, 문제의 복잡성에 머리 쥐어 뜯는 C++ 프로그래머가 증가합니다 (끝)

 

이 이하는 바톤이 넘어온 경로 (좀 과하게 길어서 접음; 아마 최장 포스팅일듯함(…))

 

Continue reading 지정문답: "boost 라이브러리"

C++로 지연된 함수콜 구현하기

지난 2일 간

관해서 포스팅했다. 오늘 이걸 실제로 광범위하게 적용시켜봤는데, 그러다 보니 문제점들이 확실히 보였다(…).

테스트 하는 동안 가장 문제가 되었던 것은 참조형으로 넘겨준 변수 문제였다. (포인터로 넘겨주는 일은 "없다고 가정하는 상황"이라서 일단 그 문제는 넘어가자 :p )

class A : private boost::noncopyable
{
public:
    A() {}
    virtual ~A() {}    
    void Do3( int a, int b, int& c ){
        cout << "A::Do3( " << a << ", " << b << ", " << c << " )" << endl;
    }    
};
 
void foo()
{
    A a;
    int tmp_val = 3;
    shared_ptr<Closure> c = MakeClosure< A, void, int, int, int& >( a, &A::Do3, 1, 2, tmp_val );
    c->Execute();
    tmp_val = 0;
    c->Execute();
}

 
foo 의 실행결과는 다음과 같다. (foo에서 Closure를 만드는 부분의 타입 추론 문제 때문에 템플릿 인자를 명시적으로 썼다)

Continue reading C++로 지연된 함수콜 구현하기

Boost::preprocessor 로 템플릿 코드 생성

어제 일종의 지연된 함수 호출 클래스 구현에 관해서 포스팅을 했다. (제한된 Closure라기보단 이 쪽 의미에 가까운 것 같다) 거기에다가 "뭔가 인자 수에 따라 다 정의해야 하는 것을 피할 방법이 없는가?" 라고 썼었는데, Boost 라이브러리에 있는 매크로 메타프로그래밍 라이브러리인 Boost::PP를 소개 받았다.

일단 boost::preprocessor를 사용해서 구현한 코드는 다음과 같다.

#define ClosureMemberDecl( z, n, unused ) A##n  m_A##n;
#define ClosureMemberEnum( z, n, unused ) A##n  a##n
#define ClosureMemberInit( z, n, unused ) m_A##n( a##n )
// Create Closure0, Closure1, ... 
#define ClosureClassDecl( z, n  ) \
template< typename T, typename R BOOST_PP_COMMA_IF(n)\
    BOOST_PP_ENUM_PARAMS( n, typename A ) > \
class Closure##n : public Closure \
{\
    protected:\
        typedef boost::function<R (T& BOOST_PP_COMMA_IF(n)\
                BOOST_PP_ENUM_PARAMS( n, A ) )> FT;\
        T&  m_Obj;\
        FT  m_Function;\
        BOOST_PP_REPEAT( n, ClosureMemberDecl, ~ )\
    public:\
        Closure##n( T& obj, FT func BOOST_PP_COMMA_IF(n) \
               BOOST_PP_ENUM( n, ClosureMemberEnum, ~ ) ) \
            : m_Obj(obj), m_Function(func) BOOST_PP_COMMA_IF(n)\
            BOOST_PP_ENUM( n, ClosureMemberInit, ~ )    { } \
        virtual ~Closure##n() { }\
        virtual void Execute() { \
            m_Function( m_Obj BOOST_PP_COMMA_IF( n ) \
                    BOOST_PP_ENUM_PARAMS( n, m_A ) );\
        }\
};
 
 

좀 길어서 접는다(…)

Continue reading Boost::preprocessor 로 템플릿 코드 생성

C++기반의 closure

요즘 하고 있는 일 중에 굉장히 제한적으로 사용되는 closure를 구현할 일이 생겼다 — 물론 C++ 기반이다. Python기반이면 이런 걱정은 안해도. Orz

Closure를 정의하자면, (from wikiepdia)

In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables.

이런 애가 되는데, 좀 더 내가 필요로하는 응용에서 생각하면(내 멋대로), 대략 함수와 이를 호출하기 위한 데이터(특히나 lexical 정보들)이 될 것이다. (물론 실제 컴퓨터공학/전산 과학적인 의미는 이와 차이가 있다)

개략적으로 그려보면 이렇게 생겨먹은 Closure 객체 인스턴스를 만들어낼 수 있으면 된다. Execute가 호출되면 Closure에 담겨있던 환경에 따라 "특정 함수"가 실행되는 그런 객체.

class Closure abstract
/// abstract base for closure implemenataion
{
public:
    Closure() { }
    virtual ~Closure() { }
    virtual void Execute() = 0;
};

 

물론 이렇게 기반 클래스를 만든 것은 C++ 타입 시스템 때문이다[…].

실제로 필요한 일을 표현하자면,

  • Persistent한 – 최소한 특정 closure가 사용되는 동안은 – 객체가 있고, 이 객체의 특정 멤버 함수를 호출한다
  • 호출 시점은 미리 정의되지 않는다. 다만 객체가 지워지기 전에 언젠가는 호출 될 수 있다
  • 객체에 묶여있지 않은 – 멤버변수가 아닌 – 인자들이 다수 존재한다
  • Lexical 환경의 변수값들은 쓰지 않는다 (이러면 굉장히 기능없고/제약없는 애가 된다)

대충 이정도다. 사실 C++에서는 함수 포인터를 보관할 편한 방법을 제공하지 않기 때문에, 특히나 멤버 함수 쯤 되면 특정 객체의 타입에 묶인 타입이 되서 더더욱 -_- 사용하기가 괴롭다. 그래서 동원한 방법은 C++의 딱풀(…) boost 라이브러리. boost::function 혹은 boost::functional 이나 boost::mem_fn 이 "함수의 일반적인 타입으로의 변환"을 가능케 해준다.

Continue reading C++기반의 closure

오늘의 삽질: boost::shared_ptr<T> 이렇게 쓰면 망한다!

강력하고 C++ 패러다임에 잘 부합하기로 유명한 라이브러리로 Boost의 일련의 라이브러리들이 있다. 그 중에서도 유명한 것을 꼽으라면 STL의 generic programming 패러다임 중 특히 함수 객체(함수자; function object; functor)를 잘 활용하게 해주는 boost::lambda 라거나, 정규식 팩키지로 사용되는 boost::regex, 그리고 이 포스팅의 주인공(…)인 boost::shared_ptr 를 포함한 스마트 포인터 팩키지가 있다. 물론 이외에도 C++ TR1 에 포함된 많은 라이브러리들이 있으니 직접 http://boost.org 를 방문해 볼 것을 권한다 :)

Boost의 메모리 관련 라이브러리 중에서도 포인터의 참조 카운트에 기반해서 동작하는 스마트 포인터인 shared_ptr<T>는 그 편리함과 거의 포인터에 유사한 동작을 제공하며, 별도의 라이브러리를 빌드할 필요도 없고 단순히 헤더파일만 추가해주면 되서 쓰기가 편하다. 용법도 지극히 단순하다.

class ResourceType; // 이런 타입이 있다고 치자.
shared_ptr<ResourceType> ptr( new ResourceType( blah, blah, blah ) );

이제 이 ptr 라는 변수는 다른 shared_ptr<ResourceType> 에 복사하거나 하면서 총 참조된 횟수를 내부적으로 관리하게 된다. 사실 이것만 보면 별 것 없는 것 같지만, 이 스마트 포인터는 정확히 같은 shared_ptr<T> 타입의 변수(가리키는 주소가 아니라 껍데기인 shared_ptr<T>의 주소가 같은 경우만)에 대한 대입 연산을 빼고 모든 연산에 대해 thread-safe 하다†. 그리고 스마트 포인터 자체에 대한 연산(가리키는 주소 말고)들은 모두 lock-free한 연산을 사용한다 – 물론 가능한 플랫폼의 범위 내에서지만, 우리가 많이 사용하게 되는 환경은 대부분 포함된다.

스마트 포인터이기 때문에 그 수명 주기를 파악하기가 매우 힘든 서버 내 객체들의 유효성을 보장하는 방식으로 이용되기도 쉽다. 즉, 특정 객체가 지워지지 않는다는 것이 보장되려면 해당 객체에 대한 shared_ptr<T> 변수가 살아있기만 하면 된다†.  이를 이용해서 특정 서버 기능이 동작하는 동안 해당 객체가 살아있다는 것을 손쉽게 보장할 수 있게 된다.

사실 이런 용도 때문에 도입된 것인데 – 이전에도 home-made 버젼의 스마트 포인터가 있었는데 좀 부족한 부분이 있어서 라이브러리 단에 해당하는 일부분에 시험적으로 도입하는 중이다 – 월요일 오후에 작업했던 부분에서 살아있어야 하는 객체가 미리 죽어버리는(메모리에서 지워지는) 현상이 일어나서 이를 디버깅했다.

문제는 멀티스레딩과 관련없는 곳에서 발생했다 – 그래서 그나마 찾기 쉬웠다. 사실 이건 바보짓의 결과였지만(…). 버그를 일으킨 코드를 간단히 쓰면 이렇다.

// m_x는 수명주기가 매우 긴 어떤 객체의 멤버 변수
m_x = shared_ptr<X>( new X() );

// blah blah blah

// X type의 멤버 함수 코드 안에서,
foobar( shared_ptr<X>( this ) ); // X의 어떤 멤버 함수 안이므로 this는 X* const 타입이됨

바로 감이 오는가? rein 같은 삽질을 하면 안된다(…).

Continue reading 오늘의 삽질: boost::shared_ptr<T> 이렇게 쓰면 망한다!

boost::python

한 3주 전? 정도부터 Python/C++을 같이 쓰려고 삽질 중이었는데, 지난 주 초에 boost::python을 발견하고 이 둘을 쉽게 연결하기 시작 – 이어지는 내용은 UPnL 워크샵(2007/6/16)에서 발표했던 것의 요약입니다 (모든내용이다 나온 것은 아닙니다).그 내용을 기반으로 해서몇일 정도 포스팅을 하려고 합니다.

사용하려는 플랫폼에 대해 초보적인 수준의 library사용 경험만 있으면(동적바인딩 라이브러리 빌드/링킹 관련) boost::python을 사용했을때 편한 점들은 일단,

  • Python C interface를 상세히 알지 못하는 상황에서도 C++ 인터페이스를 python module로 내보내기 쉽습니다.
  • Python embedding – C++ 쪽에서 python 코드를 실행시키는 것 – 을 쉽게 구현할 수 있습니다.
  • 양자를 통합해서 사용할 때도 편합니다. (예를 들어 C++이 전체 초기화를 하고, 일부분을 python 쪽에 노출 시키고, 메인 루프의 논리적인 부분은 python에서 노출된 C++ 함수를 호출함으로써 처리)

정도 입니다. 사실 3번째 경우가 필요한 상황이었는데, python 문서만 봐선 잘 할 수가 없던 부분이 쉽게 되서 매우 만족스럽습니다.

매우 긴 컴파일 시간 – <boost/python.hpp>의 컴파일 시간이 precompiled header를 써서 줄이지 않으면 안될 정도의 수준입니다 – 문제가 있고, boost library를 빌드할때 사용했었던 Python.h 버젼을 기준으로 실행 가능한 python 인터프리터가 결정되는 문제도 있습니다 – boost library를 python 2.4 헤더를 가지고 만들었었는데, 2.5 헤더와 2.5 라이브러리 파일로 빌드했더니 빌드는 되지만 실제로 import할때 해괴한-_-에러를 내면서 인터프리터가 강제 종료됩니다.

Continue reading boost::python