开发者

Policy based design and defaults

Hard to come up with a good title for this question. What I really need is to be able to provide template parameters with different number of arguments in place of a single parameter. Doesn't make a lot of sense so I'll go over the reason:

template < typename T, template <typename,typename> class Policy = default_policy >
struct policy_based : Policy<T, policy_based<T,Policy> >
{
  // inherits R Policy::fun(arg0, arg1, arg2,...,argn)
};

// normal use:
policy_base<type_a> instance;

// abnormal use:
template < typename PolicyBased > // No T since T is always the same when you use this
struct custom_policy {};

policy_base<type_b,custom_policy> instance;

The deal is that for many abnormal uses the Policy will be based on one single type T, and can't really be parameterized on T so it makes no sense to take T as a parameter. For other uses, including the default, a Policy can make sense with any T.

I have a couple ideas but none of them are really favorites. I thought that I had a better answer--using composition instead of policies--but then I realized I have this case where fun() actually needs extra information that the class itself won't have.

This is like the third time I've refactored this silly construct and I've got quite a few custom versions of it around that I'm trying to consolidate. I'd like to get something nailed down this time rath开发者_如何学Cer than just fish around and hope it works this time. So I'm just fishing for ideas right now hoping that someone has something I'll be so impressed by that I'll switch deities. Anyone have a good idea?

Edit: You might be asking yourself why I don't just retrieve T from the definition of policy based in the template for default_policy. The reason is that default_policy is actually specialized for some types T. Since asking the question I have come up with something that may be what I need, which will follow, but I could still use some other ideas.

template < typename T >
struct default_policy;

template < typename T, template < typename > class Policy = default_policy >
struct test : Policy<test<T,Policy>>
{};


template < typename T >
struct default_policy< test<T, default_policy> >
{
  void f() {}
};

template < >
struct default_policy< test<int, default_policy> >
{
  void f(int) {}
};

Edit: Still messing with it. I wasn't too fond of the above since it makes default_policy permanently coupled with "test" and so couldn't be reused in some other method, such as with multiple templates as suggested below. It also doesn't scale at all and requires a list of parameters at least as long as "test" has. Tried a few different approaches that failed until I found another that seems to work so far:

template < typename T >
struct default_policy;

template < typename T, template < typename > class Policy = default_policy >
struct test : Policy<test<T,Policy>>
{};

template < typename PolicyBased >
struct fetch_t;

template < typename PolicyBased, typename T > struct default_policy_base;

template < typename PolicyBased >
struct default_policy : default_policy_base<PolicyBased, typename fetch_t<PolicyBased>::type> {};

template < typename T, template < typename > class Policy >
struct fetch_t< test<T,Policy> > { typedef T type; };

template < typename PolicyBased, typename T >
struct default_policy_base
{
  void f() {}
};

template < typename PolicyBased >
struct default_policy_base<PolicyBased,int>
{
  void f(int) {}
};


I had a similar problem, and did not find a desirable answer. From what I know, C++ doesn't have elegant support for variable amounts of template arguments, so you have to get around it by "wrapping" extra arguments in another class;

policy_base< twoArgs<part1, part2> >

You can pretty this up a little bit by putting making typedefs for twoArgs, but not by a lot in this basic case. (Remember, you can "do" templated typedefs by using member typedefs in templated classes.)

or by having many different declarations of the core template, with different args;

template< typename T1 > struct base {...}
template< typename T1, typename t2 > struct base {...}
//etc


Have you looked at so-called Named Template Parameters? This allows you to have many parameters with a hidden default, each of which can be overridden by name (i.e. in arbitrary order). It's a trick relying on multiple levels of indirection and multiple inheritance. It's described in chapter 16.1 of the book "Templates the Complete Guide" by Vandevoorde & Josuttis. For an online exposition see here. The idea is implemented in Boost.Parameter

Here's a short summary for the Boost.Parameter library.

Step 1) you declare each parameter par0 through parN through the following macros:

BOOST_PARAMETER_TEMPLATE_KEYWORD(par0)
BOOST_PARAMETER_TEMPLATE_KEYWORD(par1)
// ... 
BOOST_PARAMETER_TEMPLATE_KEYWORD(parN)    

Each macro will define both a regular class par0 in namespace tag and a class template par0 at namespace scope

namespace tag { struct par0; } // keyword tag type
template <class T>
struct par0
: 
    parameter::template_keyword<tag::par0, T>
{};

Step 2) you declare your class signature with the required and optional parameters:

using boost::mpl::_;

typedef parameter::parameters<
    parameter::required<tag::par0>
  , parameter::optional<tag::par1>
  // ...
  , parameter::optional<tag::parN>
> your_signature;

Step 3) you declare your policy class

template <
    class a0
  , class a1 = parameter::void_
  // ...
  , class aN = parameter::void_
>
struct your_policy
{
    // Create ArgumentPack
    typedef typename
      your_signature::bind<a0, a1, /* ... */ ,aN>::type
    args;

    typedef typename parameter::value_type<
      args, tag::par0>::type par0;

    typedef typename parameter::value_type<
      args, tag::par1, your_par1_default>::type par1;

    // ...

    typedef typename parameter::value_type<
      args, tag::parN, your_parN_default>::type parN;
};

Step 4) define your policies and specializations

template<typename T>
class default_policy
:
    your_policy<T> // all hidden template parameters equal to their defaults
{};

template<typename T>
class some_par2_specialized_policy
:
    your_policy<T, par2<some_override_for_par2> > // note par1 does not have to be overriden!!
{};

For a flexible and scalable policy-based design, my experience is that Boost.Parameter works like a charm (you might need to override BOOST_PARAMETER_MAX_ARITY if you have more than 8 hidden parameters though).

It should be possible to adapt your design to this technique. Of course, once you introduce extra template parameters that have hidden defaults, you need to pick off the cases where they are overridden the code that uses your policy class. This is no different than when you use multiple traits to communicate your specialized behavior to client code, except that with policies you have everything nicely wrapped together.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜