Skip to content


C++ template 함수의 타입 추론

몇 일 전에 연이어서 C++로 일종의 지연된 함수호출 객체를 만드는 코드에 관해 포스팅했었다. (#1, #2, #3 참조)

마지막 포스팅에서도 해결 못했던 문제가 아래 코드에 나와있는 템플릿 함수를 사용할 때,

template< typename T, typename R , typename A0 , typename A1 >
Closure* MakeClosure( T& obj, R (T::*func)( A0 a0 , A1 a1 ) , A0 a0 , A1 a1 )
{
    return new Closure2 < T, R , A0 , A1> ( obj, func , a0 , a1 );
} 

void Foo::Bar( int&, int, int ) 같은 함수를 func 자리에 전달할 경우에 생기는 문제였다. C++에서 템플릿 함수의 인자 T를 다음과 같은 타입으로 추정한다. (From IBM linux Compiler page;  해석법†)

T, const T, volatile T, T&,  T*, T[10], A<T>, C(*)(T), T(*)(), T(*)(U), T C::*, C T::*,

T U::*, T (C::*)(), C (T::*)(), D (C::*)(T), C (T::*)(U), T (C::*)(U), T (U::*)(), T (U::*)(V),

E[10][i],  B<i>, TT<T>, TT<i>, TT<C>

위에서 사용한 Foo::Bar() 함수는 MakeClosure 템플릿 함수에서 T, T& 로 해석될 여지를 가지고 있기 때문에‡ "모호한 타입: int/int& 일 수 있음" 이란 이유로 컴파일이 안되었다. 해결책을 놓고 좀 고민했는데, 이미 해답을 알고있던 분이 있더라

발견한 해답은 아주 단순한 형태.


Dependent type (특정 템플릿에 의존적인 타입)의 경우에는 컴파일러가 해석하려 시도하지 않고  문장 그대로 이해하기 때문에 이를 이용해서 타입을 강제하는 것. 즉 이런 코드를 넣어서 문제를 회피했다.

/// DependentType<T>::type is a 'dependent type'
template <typename T> struct DependentType { typedef T type; }; 

이제 MakeClosure에서 그냥 A0, A1, … 대신 DependentType<A0>::type, DependentType<A1>::type … 을 사용하면 문제 해결. 최종적인 코드는 밑에.

 

///@file Closure.h : implments Closure template class
#include <boost/preprocessor.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
using boost::shared_ptr;
using boost::function;

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

/// DependentType<T>::type is a 'dependent type'
template <typename T> struct DependentType { typedef T type; }; 

#define ClosureMemberDecl( z, n, unused ) typename boost::remove_reference<A##n>::type  m_A##n;
#define ClosureMemberEnum( z, n, unused ) A##n  a##n
#define ClosureMemberEnumDependent( z, n, unused ) typename DependentType< A##n >::type  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, ClosureMemberEnumDependent, ~ ) \
        ) \
    {\
        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, ~ )

#undef ClosureDecl
#undef ClosureClassDecl
#undef ClosureMemberInit
#undef ClosureMemberEnum
#undef ClosureMemberDecl

이 상태로 구현해서 현재 작성 중인 코드에서 잘 사용하고 있다. 다만 미리 알려둘 주의 사항은, 첫번째 템플릿 인자인 T가 persistent-object 여야 한다 라는 것. 맨 첫 포스팅의 가정에서도 말하고 있는 것이지만, 호출할 객체가 사라져버리면 말짱 꽝이다. 복사하는게 의미 없는 상황을 가정하고 있어서 첫 인자(특정 멤버함수를 호출할 객체)를 참조자; reference로 전달받고 있기도하고;

*

†여기서 T, U, V는 템플릿 인자, 10은 임의의 정수 상수, i는 타입이 아닌 템플릿 인자를 나타낸다.

TT는 템플릿의 템플릿 인자(…), ( )로 둘러쌓인 것들은 함수의 매개변수 리스트가 된다. 그리고 <i>는 템플릿 인자 목록이지만 적어도 하나는 타입 인자가 아닐 것 (여기서 사용한 i의 의미), <C>는 다른 템플릿 매개변수로 넘겨지는 것과 연관이 없는 템플릿 인자 목록.

‡ 정확히는 [ T = int ] 로 해석하는 상황.

이 저작물은 별도로 명시하지 않은 경우, Creative Commons Attribution-Share Alike 3.0 Unported License에 따라 이용하실 수 있습니다.

Posted in Computer.

Tagged with , .


One Response

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

Continuing the Discussion

  1. 최익필의 이름없는 블로그 linked to this post on 2008/07/25

    항목 42: typename의 두가지 의미를 제대로 파악하자….

    내가 C++에 조예가 깊어서 글을 남기는 것이 아니라, Effecitve C++ 을 공부하는 사람들이 이 글을 보고, 도움이 되었으면 하는 생각과, 혹시 내가 틀린것이 있다면 지적해 주시지 않을까 란 생각…



Some HTML is OK

or, reply to this post via trackback.