U E D R S I H C RSS
ID
Password
Join
낙관적이어서 해로울 것은 없다. 나중에도 얼마든지 울 수 있으니까. ―L.S.L.


icon

Contents

1 머리글
2 소스 코드
3 미니 튜토리얼
3.1 구문 규칙
3.2 메타함수
3.3 컴파일 도중의 if
3.4 apply_if
3.5 apply_if, part 2
3.6 기술적 세부사항들
3.6.1 내부 구조
3.6.2 의존성
3.6.3 이식성
3.7 감사의 글
3.8 참고문헌

1 머리글 #

MPL 라이브러리는 컴파일 시점에서 알고리즘, 실행순서, 메타함수 클래스를 구현하기위한 C++ 템플릿 프레임워크입니다. MPL은 MPL 논문과 레퍼런스 가이드라는 두개의 주요 문서를 제공하고 있습니다. MPL을 처음 접하는 사용자라면 논문을 먼저 읽어보시고, 레퍼런스로 넘어가는 것을 추천합니다.

2 소스 코드 #

메인 소스코드는 boost 주 CVS 배포본에 포함되어 있습니다. 링크라이브러리는 필요하지 않으며 헤더화일만이 존재합니다.

3 미니 튜토리얼 #

3.1 구문 규칙 #

이 튜토리얼에 걸쳐서 사용되는 예제는 상세명시된 명칭을 사용합니다. 예를 들면, 그냥 vector 대신 std::vector를 사용하는 것을 들 수 있습니다. 간략 명시된 명칭은 예제 자체에서만 지역적으로 사용되는 지역 개체를 의미합니다. boost::mpl namsapce로부터의 명칭은 mpl namespace 별칭을 사용합니다(예: boost::mpl::apply대신 mpl::apply)이는 다음과 같은 명령문을 통하여 구현할 수있습니다.
namespace mpl = boost::mpl;
라이브러리에서는 이러한 기능을 제공하기위해서 boost/mpl/alias.hpp 헤더를 제공하고 있습니다. (이 헤더를 include하면 위와같이 선언한 것과 같은 효과가 있습니다)

3.2 메타함수 #

MPL내에서 함수와 동일한 기능을 담당하는 메타 프로그래밍 요소는 내부에 "type"이라는 typedef 맴버를 가진 클래스 템플릿입니다.
// 가장 간단한 예제. 그다지 유용하진 않다.
template< typename T >
struct identity 
{
    typedef T type;
};

// 아마도 더 유용한 예제
template< typename T >
struct result_type 
{
    typedef typename T::result_type type;
};

메타함수를 "실행"하는 것은 간단하게 특별한 템플릿 매개변수(메타함수 "arguments")를 가진 클래스 템플릿을 인스턴스화하고, 그 인스턴스에 내장된 type 맴버를 통하여 결과를 얻는 것을 말합니다.
typedef identity<int>::type t1; // t1 == int
typedef result_type< std::unary_function<int,bool> >::type t2; // t2 == bool

3.3 컴파일 도중의 if #

대부분의 흥미로운 템플릿 메타 프로그램들은 종종 많은 판단문 코드를 담고 있습니다. 몇몇 상태결정/동작은 (부분적인) 클래스 템플릿 정의 또는 함수 오버로딩으로 구현할 수 있습니다 vel95a, Ale00 참고문헌 참조 하지만 컴파일 중간 처리구문에 기초한 두개의 타입 중 하나를 선택하는 것은 독립된 라이브러리 요소를 필요로 합니다. boost::mpl에서는 if_ 구문으로 이를 지원합니다.
template< typename T >
struct heap_holder
{
 // ... 
 private:
    boost::scoped_ptr<T> m_object;
};

template< typename T >
struct stack_holder
{
 // ... 
 private:
    T m_object;
};

template< typename T >
struct can_be_on_stack
    : mpl::bool_c< (sizeof(T) <= sizeof(double)) >
{
};

// 'T'맴버를 담고 있는 곳을 선택하기위해 'if_'를 사용합니다.
template< typename T >
struct lightweight
   : private mpl::if_<
          can_be_on_stack<T>
        , stack_holder<T>
        , heap_holder<T>
        >::type
{
   // ...
};

if_ 템플릿의 첫번째 템플릿 매개변수는 통합 상수 개념(Integral Constant) 모델을 따르는 타입을 지정하여야만 합니다.
template< typename T >
struct lightweight
   : private mpl::if_c<
          (sizeof(T) <= sizeof(double))
        , stack_holder<T>
        , heap_holder<T>
        >::type
{
   // ...
};

3.4 apply_if #

In run-time C++, it is guaranteed that when we reach an if statement, only one branch will be executed. Executing the branch for which the result is not required would be unnecessary and inefficient. More importantly, frequently the non-required branch is invalid, and executing it would cause an error. For instance, the following code would be badly broken if both branches of the statement were evaluated:
void fun(giraffe* g)
{
    if (g)
        cout << g->name();
    else
        cout << "no giraffe";
}

In compile-time world, things are different. Which parameters to if_ template are instantiated is determined by the form of each template parameter and the corresponding language rules (ISO98, section 14.7.1), not by the value of the compile-time expression being switched on. That means that if, in attempt to process a particular if_ construct, the compiler determines that one of its “branch” template parameters is ill-formed, it will issue a diagnostics even if the value of compile-time expression would lead to “choosing” the other, valid parameter type.

To clarify what we just said, here is a broken first attempt at writing a pointed_type metafunction, that when instantiated for a T that is either a plain pointer or a smart pointer, "returns" the pointed type:
template< typename T >
struct pointed_type
{
    typedef typename mpl::if_<
          boost::is_pointer<T>
        , typename boost::remove_pointer<T>::type
        , typename T::element_type // #1
        >::type type;
};

typedef pointed_type< std::auto_ptr<int> >::type int_ptr; // ok
typedef pointed_type<char*>::type char_ptr; // error in line #1!

If we try to compile the above, we will get something like this:
Error: name followed by "::" must be a class or namespace name
because the expression typename T::element_type is not valid in case of T == char*.

Here's what we need to do to make pointed_type work for plain pointers: 1 instead of instantiating our two potential results before passing them to if_, we need to write metafunctions that can be used to instantiate the results; then we can use if_ to choose a metafunction, and only then should we use that function to get the result.

boost::remove_pointer already is a metafunction. We just need to write an auxiliary function to return the element_type of a pointer type:
namespace aux {
template< typename T >
struct element_type
{
     typedef typename T::element_type type;
};
}

Now we can select the metafunction to call based on the result of boost::is_pointer, and then apply it to form the result:

template< typename T >
struct pointed_type
{
 private:
    // pick a metafunction
    typedef typename mpl::if_<
          boost::is_pointer<T>
        , boost::remove_pointer<T>
        , aux::element_type<T>
        >::type func_; // #1

 public:
    // apply the metafunction
    typedef typename func_::type type;
};
The key knowledge that makes the above viable is that in line #1 the compiler is guaranteed not to instantiate boost::remove_pointer<T> and aux::element_type<T> templates, - even although they are passed as actual arguments to the if_.

The described technique is so common in template metaprograms, that it makes sense to facilitate the selection of the nested type member by introducing a high level equivalent to if_ that will do func_::type operation as a part of its invocation. The MPL provides such a template - it's called apply_if. Using it, we can re-write the above code as simply as:
[
template< typename T >
struct pointed_type
{
    typedef typename mpl::apply_if<
          boost::is_pointer<T>
        , boost::remove_pointer<T>
        , aux::element_type<T>
        >::type type;
};

3.5 apply_if, part 2 #

Besides solving the “making the code compile” problem, the apply_if technique we've just learned can be also used to improve metaprogram efficiency.

Suppose we want to define a high-level wrapper around boost::remove_pointer traits template, which will strip the pointer qualification conditionally. We will call it remove_pointer_if:

template<
      typename Condition
    , typename T
    >
struct remove_pointer_if
{
    typedef typename mpl::if_<
          Condition
        , typename boost::remove_pointer<T>::type
        , T
        >::type type;
};

The above works the first time, but it's not the most optimal implementation. Similar to our previous examples, boost::remove_pointer<T> gets instantiated even if its result is never used. In the metaprogramming world compilation time is an important resource Abr01, and it is wasted by unnecessary template instantiations.

Let's see what we need to substitute if_ by apply_if here. We already have one metafunction to pass to apply_if - boost::remove_pointer<T>, but we need a second one, - let's call it f, - such as f<T>::type == T. We could write this one ourselves, but fortunately MPL already provides us with a template that matches this exact definition - it's called identity. Applying this knowledge, we get:

template<
      typename Condition
    , typename T
    >
struct remove_pointer_if
{
    typedef typename mpl::apply_if<
          Condition
        , boost::remove_pointer<T>
        , mpl::identity<T>
        >::type type;
};

3.6 기술적 세부사항들 #

3.6.1 내부 구조 #

The library provides you with a fine-grained header structure with one header per public component (class/function template), with the header named after the component; for example, boost::mpl::apply<> template is defined in the header boost/mpl/apply.hpp. This scheme both ensures that you don't pay for what you don't use in terms of compilation time/header dependencies, and frees you from memorizing/looking up header/component correspondence. Several composite headers for the entities that are likely to be used together (e.g. logical operations - logical_or, logical_and, etc.) are also provided. It allows one to avoid the burden of spelling many #include directives in programs that make an intensive use of the library facilities. 2

3.6.2 의존성 #

Besides boost/config.hpp header, the MPL heavily depends on two other Boost libraries - the Boost Preprocessor library PRE, and the Type Traits library TTL. These dependencies are essential and cannot be eliminated. In addition to those, the boost/mpl/assert_is_same.hpp header depends on Boost Static Assert library SAL. The library tests and examples may depend on some additional Boost libraries, e.g. Boost Bind BBL; you don't have to have those unless you are interested in actually compiling the tests/examples (probably you are, though).

3.6.3 이식성 #

MPL은 다음과 같은 컴파일러에서 테스트되었습니다.
  • Microsoft Visual C++ 6.0, SP 5
  • Microsoft Visual C++ .NET (7.0)
  • Metrowerks CodeWariror 7.2/8.1
  • Intel C++ Compiler 5.0, 6.0
  • GCC 2.95.3-5
  • GCC 3.1
  • Comeau C/C++ 4.2.45/4.3.0
  • Borland C++ 5.5.1

아직 완성되지 않은 현재 테스트 컴파일 결과는 [http]http://www.mywikinet.com/mpl/log.html에서 확인할 수 있습니다.

3.7 감사의 글 #

Following is a list of people who in one or another way contributed to the library development. The list is work in progress!
David Abrahams, Emily Winch, Eric Friedman, Vesa Karvonen, Peter Dimov, Mat Marcus, Fernando Cacciola, Paul Mensonides, David B. Held, John Bandela, Arnaldur Gylfason, Hamish Mackenzie.
이 문서의 원저작권은 Aleksey Gurtovoy, David Abrahams, Emily Winch에게 있습니다.

3.8 참고문헌 #


Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2010-10-28 12:42:52
Processing time 0.4845 sec