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 ) );\
        }\
};
 
 

좀 길어서 접는다(…)

 
// Create ClosureHelperDecl for corresponding Closure class
#define ClosureHelperDecl( z, n ) \
    template< typename T, typename R BOOST_PP_COMMA_IF(n)\
        BOOST_PP_ENUM_PARAMS( n, typename A ) >\
        boost::shared_ptr<Closure> MakeClosure( T& obj, \
            R (T::*func)( BOOST_PP_ENUM( n, ClosureMemberEnum, ~ ) ) \
            BOOST_PP_COMMA_IF(n)\
            BOOST_PP_ENUM( n, ClosureMemberEnum, ~ ) \
        ) \
    {\
        return boost::shared_ptr<Closure>( new Closure##n < T, R BOOST_PP_COMMA_IF(n) \
            BOOST_PP_ENUM_PARAMS( n, A )>\
            ( obj, func BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS( n, a ) ) ); \
    }

#define ClosureDecl( z, n, unused ) \
    ClosureClassDecl( z, n ) \
    ClosureHelperDecl( z, n )

// 최대 인자 수를 다음 매크로의 첫인자로
BOOST_PP_REPEAT( 5, ClosureDecl, ~ )

 

BOOST_PP_ 로 시작되는 매크로들이 boost/preprocessor.hpp 에 정의되어 있는데, 이 매크로들을 사용해서 template 정의를 "반복" 시켜버린 상황이다.  코드가 반복되지 않아도 되는 것은 좋은데, 유지보수와는 한 천광년쯤 먼 곳에 와버린 기분.

다만 실제로 생성되는 코드 – 그러니까 C/C++ 전처리기를 통과시킨 후의 결과 – 는 별도의 방법으로 확인할 수 있긴 하기 때문에 작업 진행은 코드 생성기를 작성하는 것보다약간 편하다. 예를 들어 위 코드를 전처리기로 처리하고 나면 이런 녀석이 나온다.

boost preprocessor 라이브러리에 대한 인상을 정리해보면 boost::mpl 처럼 C++ 코드를 C++ 도메인 안에서 생성 할 수 있게 해준다는 "엄청난 장점" 이 있다. 일일이 코드 제네레이터를 "만들거나", "실행시키는 것"은 귀찮은 일이다.

다만 boost mpl도 그렇지만 이 라이브러리 역시 유지/보수에는 부적합할 수 있다. 손으로 튜닝해야 할 일이 어느 정도 있는 부분에선 사실 상 쓸 수 없을 것 같기도 하다. C++ template meta programming 을 처음 접했을 때처럼 "벽"이 있는 상태고, mpl과는 달리 코드를 읽는 것도 어렵다. C++ 내의 언어이긴 하지만 매크로 자체가 C++ 문법과는 좀 이질적이기 때문에 쉽사리 손댈 수 없는 코드를 작성하게 될 것 같다 -_-;;

다만 template과 template meta programming으로 해결할 수 없는 "비슷한 템플릿의 반복"을 해결하는 방법으로는 이게 가장 유용한 방법인 것 같다. 한 번 작성하고 그 이후엔 유지 보수가 상대적으로 필요치 않다면 대안인 "코드 반복 작성", "코드 생성기 작성" 보다 뭔가 더 나은 것을 주는 것 같다. 이 부분은 다음 번에 포스팅하기로 하고(…)

결론적으로,

용자 rein은 boost::preprocessor 사용법을 습득했다!

 

ps. 조금 생각해보니 boost::preprocessor를 현재 사용 중인 패킷 처리 코드 생성기 부분에도 쓸 수 있을 것 같다.

Published by

rein

나는 ...

6 thoughts on “Boost::preprocessor 로 템플릿 코드 생성”

  1. 으악!!! boost_pp를 처음 배울 때 너무 어려워서 즐치고 매크로로 발랐었는데 ‘생각보다는’ 훨씬 readable하잖아! orz

  2. 함수형 언어는 syntactic sugar를 추가하고,
    절차형/객체지향 언어들은 함수형 언어의 개념을 가져가는게 대세(?)인듯.

Leave a Reply