1.Parameter库简介
Boost Parameter库为函数和模板类的参数传递提供方便,支持以名字传递参数,而不是按照普通的参数书写位置顺序传递参数,主要用于参数较多且复杂的情况。本文的例子都是来自于Boost Parameter库官网:
先看一下官网提供的例子程序:
window* new_window(
char const* name,
int border_width = default_border_width,
bool movable = true,
bool initially_visible = true
);
这个函数有四个参数,其中三个提供默认值。在阅读和调用时,如果不清除每个参数具体的含义,在阅读这样的代码时就会很吃力:
window* w = new_window("alert", 1, true, false);
而且我们通常只关心其中某几个参数,其他参数只要指定默认值就可以了。这时我们需要的是根据参数名字来指定参数值,而不需要记住每个参数的顺序和含义:
window* w = new_window(movable_=false, "alert box"); // OK!
window* w = new_window("alert box", movable_=false); // OK!
上面代码中,即使通过不同的顺序调用new_window函数,也可以执行不影响功能,而且更重要的是,这种按照名字和参数规则的方式更方便记忆和使用,这也正是Parameter库提供的。
2.模板类参数传递使用方法
Parameter库支持普通函数、类成员函数、类构造函数和模板类。这里我们只讲解模板类参数传递的使用方法。假设我们需要为类class_提供模板参数支持,需要以下几个步骤。
template class<
ValueType, BaseList = bases<>
, HeldType = ValueType, Copyable = void
>
class class_;
(1)定义类模板参数名
使用BOOST_PARAMETER_TEMPLATE_KEYWORD宏定义参数名:
BOOST_PARAMETER_TEMPLATE_KEYWORD(class_type)
BOOST_PARAMETER_TEMPLATE_KEYWORD(base_list)
BOOST_PARAMETER_TEMPLATE_KEYWORD(held_type)
BOOST_PARAMETER_TEMPLATE_KEYWORD(copyable)
(2)定义类模板参数签名
使用::boost::parameter::parameters定义类模板参数签名,使用required和optional指定参数缺省规则(是否必须传递一个参数),使用deduced指定参数推导规则。代码示例如下:
typedef ::boost::parameter::parameters<
::boost::parameter::required<tag::class_type, is_class<_> >
, ::boost::parameter::optional<
::boost::parameter::deduced<tag::base_list>
, is_base_and_derived<detail::bases_base,_>
>
, ::boost::parameter::optional<
::boost::parameter::deduced<tag::held_type>
, mpl::not_<
mpl::or_<
is_base_and_derived<detail::bases_base,_>
, is_same<noncopyable,_>
>
>
>
, ::boost::parameter::optional< ::boost::parameter::deduced<tag::copyable >, is_same<noncopyable,_> >
> class_signature;
(3)定义模板类
使用value_type定义模板类的各个参数,用于按名字指定参数。代码示例如下:
template <
class A0
, class A1 = parameter::void_
, class A2 = parameter::void_
, class A3 = parameter::void_
>
struct class_
{
// Create ArgumentPack
typedef typename
class_signature::bind<A0,A1,A2,A3>::type
args;
// Extract first logical parameter.
typedef typename parameter::value_type<
args, tag::class_type>::type class_type;
typedef typename parameter::value_type<
args, tag::base_list, bases<> >::type base_list;
typedef typename parameter::value_type<
args, tag::held_type, class_type>::type held_type;
typedef typename parameter::value_type<
args, tag::copyable, void>::type copyable;
class_type first_param;
base_list second_param;
held_type third_param;
};
定义后,可以按照名字来指定参数,示例代码如下:
typedef boost::python::class_<B> C0;
typedef boost::python::class_<B, boost::noncopyable> C1;
typedef boost::python::class_<E, D, boost::python::bases<B> > C2;
int main()
{
C0 c0;
C1 c1;
C2 c2;
std::cout << " c0 first data: " << c0.first_param.data << std::endl;
std::cout << " c0 third data: " << c0.third_param.data << std::endl;
std::cout << " c1 first data: " << c1.first_param.data << std::endl;
std::cout << " c2 first data: " << c2.first_param.data << std::endl;
std::cout << " c2 second data: " << c2.second_param.data << std::endl;
std::cout << " c2 third data: " << c2.third_param.data << std::endl;
return 0;
}
完整示例代码下载链接。从输出可以看到,程序能够根据参数类型和推导规则正确识别参数传递顺序,输出正确结果:
3.模板类参数传递代码分析
接下来是代码分析,这一部分比较枯燥复杂(看过Boost源码的同学一定深有体会),需要不断跟踪代码,由于里面的宏定义和typedef实在太多太复杂,这里只分析最关键的一部分。
(1)parameter::value_type分析
template <class Parameters, class Keyword, class Default = void_>
struct value_type
{
typedef typename mpl::apply_wrap3<
typename Parameters::binding,Keyword,Default,mpl::false_
>::type type;
BOOST_MPL_ASSERT_NOT((
mpl::and_<
is_same<Default, void_>
, is_same<type, void_>
>
));
}
接下来看apply_wrap3:
template<
typename F, typename T1, typename T2, typename T3
>
struct apply_wrap3
: F::template apply< T1,T2,T3 >
{
};
所以这里的关键是参数F的apply函数,具体看下面的分析
(2)模板类参数集合args分析
模板类中通过class_signature::bind定义参数集合,class_signature::bind定义如下:
template <
BOOST_PP_ENUM_BINARY_PARAMS(
BOOST_PARAMETER_MAX_ARITY, class A, = void_ BOOST_PP_INTERCEPT
)
>
struct bind
{
typedef typename aux::make_arg_list<
typename BOOST_PARAMETER_build_arg_list(
BOOST_PARAMETER_MAX_ARITY, aux::make_items, PS, A
)::type
, deduced_list
, aux::tag_template_keyword_arg
>::type result;
typedef typename mpl::first<result>::type type;
};
接下来看make_arg_list:
template <
class List
, class DeducedArgs
, class TagFn
, class EmitErrors = mpl::true_
>
struct make_arg_list
{
typedef typename make_arg_list_aux<
List, DeducedArgs, TagFn, mpl::true_, aux::set0, empty_arg_list, void_
>::type type;
};
接下来看make_arg_list_aux:
template <
class List
, class DeducedArgs
, class TagFn
, class Positional
, class DeducedSet
, class ArgumentPack
, class Error
>
struct make_arg_list_aux
{
typedef typename mpl::eval_if<
is_same<List, void_>
, mpl::identity<mpl::pair<ArgumentPack, Error> >
, make_arg_list0<List, DeducedArgs, TagFn, Positional, DeducedSet, ArgumentPack, Error>
>::type type;
};
接下来看eval_if:
template<
typename BOOST_MPL_AUX_NA_PARAM(C)
, typename BOOST_MPL_AUX_NA_PARAM(F1)
, typename BOOST_MPL_AUX_NA_PARAM(F2)
>
struct eval_if
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) \
|| ( BOOST_WORKAROUND(BOOST_MPL_CFG_GCC, >= 0x0300) \
&& BOOST_WORKAROUND(BOOST_MPL_CFG_GCC, BOOST_TESTED_AT(0x0304)) \
)
{
typedef typename if_<C,F1,F2>::type f_;
typedef typename f_::type type;
#else
: if_<C,F1,F2>::type
{
#endif
BOOST_MPL_AUX_LAMBDA_SUPPORT(3,eval_if,(C,F1,F2))
};
看到这里,从if_<C,F1,F2>的字面意思,大概也可以想到,这是一个条件判断的结构,根据条件C的取值,返回F1或F2,实际也是如此:
template<
typename BOOST_MPL_AUX_NA_PARAM(T1)
, typename BOOST_MPL_AUX_NA_PARAM(T2)
, typename BOOST_MPL_AUX_NA_PARAM(T3)
>
struct if_
{
private:
// agurt, 02/jan/03: two-step 'type' definition for the sake of aCC
typedef if_c<
#if defined(BOOST_MPL_CFG_BCC_INTEGRAL_CONSTANTS)
BOOST_MPL_AUX_VALUE_WKND(T1)::value
#else
BOOST_MPL_AUX_STATIC_CAST(bool, BOOST_MPL_AUX_VALUE_WKND(T1)::value)
#endif
, T2
, T3
> almost_type_;
public:
typedef typename almost_type_::type type;
BOOST_MPL_AUX_LAMBDA_SUPPORT(3,if_,(T1,T2,T3))
};
template<
bool C
, typename T1
, typename T2
>
struct if_c
{
typedef T1 type;
};
template<
typename T1
, typename T2
>
struct if_c<false,T1,T2>
{
typedef T2 type;
};
if_c使用偏特化特性,根据第一个模板参数为True或False,定义不同的type,即不同的返回值。那么回过头来看make_arg_list_aux,就可以想到,make_arg_list_aux的含义就是,参数根据List是否为空,取值为mpl::identity或make_arg_list0。
struct make_arg_list_aux
{
typedef typename mpl::eval_if<
is_same<List, void_>
, mpl::identity<mpl::pair<ArgumentPack, Error> >
, make_arg_list0<List, DeducedArgs, TagFn, Positional, DeducedSet, ArgumentPack, Error>
>::type type;
};
接下来的问题是,mpl::identity和make_arg_list0的含义分别是什么。考虑到value_type中第三个参数是默认值,并且有些optional参数可以不传进来,可以想到,一个是指的是参数默认值(没有传参数时使用默认值),一个是实际传进来的参数。
(3)mpl::identity分析
template<
typename BOOST_MPL_AUX_NA_PARAM(T)
>
struct identity
{
typedef T type;
BOOST_MPL_AUX_LAMBDA_SUPPORT(1, identity, (T))
};
可以看到,identity<>::type就是传进来的模板参数T。那么接下来看mpl::pair<ArgumentPack, Error>:
template<
typename BOOST_MPL_AUX_NA_PARAM(T1)
, typename BOOST_MPL_AUX_NA_PARAM(T2)
>
struct pair
{
typedef pair type;
typedef T1 first;
typedef T2 second;
BOOST_MPL_AUX_LAMBDA_SUPPORT(2,pair,(T1,T2))
};
可以看到,pair存储的是两个模板参数,first和second分别返回第一个和第二个模板参数。
回过头来,看到模板类参数集合args就是boost::parameter::parameters::bind::type,而bind::type是:
typedef typename mpl::first<result>::type type;
接下来看mpl::first:
template<
typename BOOST_MPL_AUX_NA_PARAM(P)
>
struct first
{
#if !defined(BOOST_MPL_CFG_MSVC_70_ETI_BUG)
typedef typename P::first type;
#else
typedef typename aux::msvc_eti_base<P>::first type;
#endif
BOOST_MPL_AUX_LAMBDA_SUPPORT(1,first,(P))
};
可以看到,这里取的是first,即第一个参数,那么mpl::pair返回的就是第一个参数ArgumentPack,对应就是empty_arg_list,接下来看empty_arg_list,这里只分析其中的binding结构(因为要用到的就是这个bind):
struct empty_arg_list
{
// A metafunction class that, given a keyword and a default
// type, returns the appropriate result type for a keyword
// lookup given that default
struct binding
{
template<class KW, class Default, class Reference>
struct apply
{
typedef Default type;
};
};
};
结合apply_wrap3的F参数的apply函数考虑,binding::type返回的就是第二个参数Default,其实就是value_type指定的第三个参数即默认参数。
简而言之,mpl::identity<mpl::pair<ArgumentPack, Error> >表示的是模板类的默认参数。
(4)make_arg_list0分析
理解identity后,可以想到,make_arg_list0就是模板类传进来的实际参数。
template <
class List
, class DeducedArgs
, class TagFn
, class Positional
, class UsedArgs
, class ArgumentPack
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
, class argument
#endif
, class Error
>
struct make_arg_list0
{
typedef typename mpl::if_<
is_same<tagged, void_>
, ArgumentPack
, arg_list<tagged, ArgumentPack>
>::type argument_pack;
typedef typename make_arg_list_aux<
typename List::tail
, DeducedArgs
, TagFn
, positional
, typename deduced_data::second
, argument_pack
, error
>::type type;
};
make_arg_list0::type的倒数第二个参数是argument_pack,即对参数的打包,argument_pack用到了arg_list,arg_list定义如下:
template <class TaggedArg, class Next = empty_arg_list>
struct arg_list : Next
{
typedef arg_list<TaggedArg,Next> self;
typedef typename TaggedArg::key_type key_type;
typedef typename is_maybe<typename TaggedArg::value_type>::type holds_maybe;
typedef typename mpl::eval_if<
holds_maybe
, get_reference<typename TaggedArg::value_type>
, get_reference<TaggedArg>
>::type reference;
typedef typename mpl::if_<
holds_maybe
, reference
, typename TaggedArg::value_type
>::type value_type;
TaggedArg arg; // Stores the argument
// Store the arguments in successive nodes of this list
template< // class A0, class A1, ...
BOOST_PP_ENUM_PARAMS(BOOST_PARAMETER_MAX_ARITY, class A)
>
arg_list( // A0& a0, A1& a1, ...
BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PARAMETER_MAX_ARITY, A, & a)
)
: Next( // a1, a2, ...
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PARAMETER_MAX_ARITY, a)
, void_reference()
)
, arg(a0)
{}
// Create a new list by prepending arg to a copy of tail. Used
// when incrementally building this structure with the comma
// operator.
arg_list(TaggedArg head, Next const& tail)
: Next(tail)
, arg(head)
{}
// A metafunction class that, given a keyword and a default
// type, returns the appropriate result type for a keyword
// lookup given that default
struct binding
{
template <class KW, class Default, class Reference>
struct apply
{
typedef typename mpl::eval_if<
boost::is_same<KW, key_type>
, mpl::if_<Reference, reference, value_type>
, mpl::apply_wrap3<typename Next::binding, KW, Default, Reference>
>::type type;
};
};
};
注意这里的binding也有一个apply,与empty_arg_list对应,前面说了,make_arg_list0就是模板类传进来的实际参数,我们需要看传进来的参数是什么样的。boost::parameter::parameters的bind中用到了BOOST_PARAMETER_build_arg_list,就是用来构建参数列表的:
#define BOOST_PARAMETER_build_arg_list(n, make, parameter_spec, argument_type) \
BOOST_PP_REPEAT( \
n, BOOST_PARAMETER_make_arg_list, (make)(parameter_spec)(argument_type)) \
mpl::identity<void_> \
BOOST_PP_REPEAT(n, BOOST_PARAMETER_right_angle, _)
这里只分析BOOST_PP_REPEAT:
# define BOOST_PP_REPEAT BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))
# define BOOST_PP_AUTO_REC(pred, n) BOOST_PP_NODE_ENTRY_ ## n(pred)
所以_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4)变成BOOST_PP_NODE_ENTRY_4(BOOST_PP_REPEAT_P)。
# define BOOST_PP_AUTO_REC(pred, n) BOOST_PP_NODE_ENTRY_ ## n(pred)
# define BOOST_PP_NODE_ENTRY_4(p) BOOST_PP_NODE_2(p)(p)
# define BOOST_PP_NODE_2(p) BOOST_PP_IIF(p##(2), BOOST_PP_NODE_1, BOOST_PP_NODE_3)
所以BOOST_PP_NODE_ENTRY_4(BOOST_PP_REPEAT_P)变成 BOOST_PP_IIF(BOOST_PP_REPEAT_P(2), BOOST_PP_NODE_1, BOOST_PP_NODE_3)(BOOST_PP_REPEAT_P)
# define BOOST_PP_REPEAT_P(n) BOOST_PP_CAT(BOOST_PP_REPEAT_CHECK_, BOOST_PP_REPEAT_ ## n(1, BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL))
# define BOOST_PP_REPEAT_2(c, m, d) BOOST_PP_REPEAT_2_I(c, m, d)
# define BOOST_PP_REPEAT_2_I(c, m, d) BOOST_PP_REPEAT_2_ ## c(m, d)
BOOST_PP_REPEAT_2变成BOOST_PP_REPEAT_2_1(BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3, BOOST_PP_NIL)
# define BOOST_PP_REPEAT_2_1(m, d) m(3, 0, d)
BOOST_PP_REPEAT_2变成BOOST_PP_NIL BOOST_PP_TUPLE_EAT_3(3, 0, d),即BOOST_PP_NIL。
# define BOOST_PP_REPEAT_CHECK_BOOST_PP_NIL 1
BOOST_PP_REPEAT_P(2)等于1。所以BOOST_PP_NODE_ENTRY_4(BOOST_PP_REPEAT_P)变成BOOST_PP_NODE_1(BOOST_PP_REPEAT_P)。
# define BOOST_PP_NODE_1(p) BOOST_PP_IIF(p##(1), 1, 2)
所以BOOST_PP_NODE_ENTRY_4(BOOST_PP_REPEAT_P)变成BOOST_PP_IIF(BOOST_PP_REPEAT_P(1), 1, 2)
BOOST_PP_REPEAT_P(1)等于1,所以BOOST_PP_NODE_ENTRY_4(BOOST_PP_REPEAT_P)等于1,即BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4)等于1。所以 BOOST_PP_REPEAT 就是 BOOST_PP_REPEAT_1。
# define BOOST_PP_REPEAT_1(c, m, d) BOOST_PP_REPEAT_1_I(c, m, d)
# define BOOST_PP_REPEAT_1_I(c, m, d) BOOST_PP_REPEAT_1_ ## c(m, d)
# define BOOST_PP_REPEAT_1_0(m, d)
# define BOOST_PP_REPEAT_1_1(m, d) m(2, 0, d)
# define BOOST_PP_REPEAT_1_2(m, d) BOOST_PP_REPEAT_1_1(m, d) m(2, 1, d)
# define BOOST_PP_REPEAT_1_3(m, d) BOOST_PP_REPEAT_1_2(m, d) m(2, 2, d)
# define BOOST_PP_REPEAT_1_4(m, d) BOOST_PP_REPEAT_1_3(m, d) m(2, 3, d)
# define BOOST_PP_REPEAT_1_5(m, d) BOOST_PP_REPEAT_1_4(m, d) m(2, 4, d)
# define BOOST_PP_REPEAT_1_6(m, d) BOOST_PP_REPEAT_1_5(m, d) m(2, 5, d)
# define BOOST_PP_REPEAT_1_7(m, d) BOOST_PP_REPEAT_1_6(m, d) m(2, 6, d)
# define BOOST_PP_REPEAT_1_8(m, d) BOOST_PP_REPEAT_1_7(m, d) m(2, 7, d)
也就是说,BOOST_PP_REPEAT( n, BOOST_PARAMETER_make_arg_list, (make)(parameter_spec)(argument_type))
是迭代展开BOOST_PARAMETER_make_arg_list 8次,展开方式为:
BOOST_PARAMETER_make_arg_list(2, 7, (make)(parameter_spec)(argument_type))
简而言之,会将传入的参数展开成一个参数列表,对于参数签名中的真正参数。