Boost Parameter库初步分析

本文深入讲解Boost Parameter库的使用方法及原理,包括如何利用参数名传递复杂参数,以及模板类参数传递的具体实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Parameter库简介    

    Boost Parameter库为函数和模板类的参数传递提供方便,支持以名字传递参数,而不是按照普通的参数书写位置顺序传递参数,主要用于参数较多且复杂的情况。本文的例子都是来自于Boost Parameter库官网:

https://www.boost.org/doc/libs/1_62_0/libs/parameter/doc/html/index.html#parameter-enabled-class-templates

    先看一下官网提供的例子程序:

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))

    简而言之,会将传入的参数展开成一个参数列表,对于参数签名中的真正参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值