From: Aleksey Gurtovoy (agurtovoy_at_[hidden])
Date: 2002-10-09 02:57:53


David B. Held wrote:
> > Fixing a missing typename
>
> Technically, my version wasn't missing a typename, because I was using
> is_same<>::value with if_c. ;)

And I wasn't referring to that one :).

        typedef typename mpl::if_c<
            ::boost::is_same<i_, mpl::end<Sequence>::type >::value,
                                 ^^^^^^^^^^^^^^^^^^^^^^^^
            make_iterator<Default>, i_
>::type::type type;

>
> > and style a little bit, here's an "approved" version :)
>
> But I do like your version better. Thanks for the tip. I
> see that apply_if combines an if_ with a dereference, which happens
> to be the same as an apply. I wouldn't have figured that out on my own.
;)

http://www.mywikinet.com/mpl/#applyif, the last paragraph:

"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. ..."

> Now, I notice that you don't bother to hide "private metavariables",
> but just make everything struct. Is that simply because it would be silly
to
> try to use any of the the intermediate results, and thus there's no point
to
> try to hide any of the implementation details?

In the library code, I usually do hide auxiliary typedefs declared directly
in the scope of a public component:

    template< typename T > struct my_algorithm
    {
     private:
        typedef /* ... */ iter_;
        typedef /* ... */ last_;
     
     public:
        typedef /* ... */ type;
    };

to denote even more explicitly that members with '_' suffix do not comprise
a public interface. I usually don't bother with it in the scope of
implementation structs:

    namespace aux {
    template< typename T > struct my_impl
    {
        typedef /* ... */ iter_;
        typedef /* ... */ last_;
        typedef /* ... */ type;
    };
    }

    // often defined in a separate header
    template< typename T > struct my_algorithm
        : aux::my_impl<T>
    {
    };

mostly because I am lazy ;).

>
> > [...]
> > A question - why BOOST_STORAGE_POLICY et al. instead of, let's say,
> >
> > typename get_storage_policy<policies_,T>::type
> >
> > ?
>
> Good question. Two reasons. One, repetition. They appear twice, and
> something as complex as this isn't something I want to spend time on
> synchronizing whenever I make a minor change (this is the
> kind of thing I'd spend an hour on diagnosing).

But from maintenance point of view the difference between

# define BOOST_STORAGE_POLICY \
    apply_lambda< \
        get_policy<policies_, storage_policy_tag, scalar_storage<_> >::type,
\
        T \
>::type

...
    class smart_ptr
        : public optimally_inherit<
            optimally_inherit<
                  BOOST_STORAGE_POLICY
...
    {
    public: // Policy types
        typedef T element_type;
        typedef BOOST_STORAGE_POLICY storage_policy;

and

    namespace aux {
    template< typename Policies, typename T >
    struct storage_policy
    {
        typedef typename get_policy<
              policies_
            , storage_policy_tag
            , scalar_storage<_>
>::type f_;
            
        typedef typename apply_lambda<f_,T>::type type;
    };
    }

...
    class smart_ptr
        : public optimally_inherit<
            optimally_inherit<
                  typename aux::storage_policy<policies_,T>::type
...
    {
    public: // Policy types
        typedef T element_type;
        typedef typename aux::storage_policy<policies_,T>::type
storage_policy;

is negligible, IMO (ignoring the fact that BOOST_STORAGE_POLICY is a macro
:).

> Two, what I didn't show is the template template version (which I left in
> for historical reasons).
> The usage is controlled by BOOST_SMART_POINTER_LEGACY_INTERFACE

OK, I see how that could warrant the existence of other macros besides
BOOST_SMART_POINTER_PARAMETERS. Thanks for elaborating.

[...]

> > [...]
> > Probably to have an associative container in MPL, but not
> > now :). Feel free to contribute, of course.
>
> Hmm...so if I understand correctly, I would add all the
> defaults to the container, then add the specified policies
> according to their categories, overwriting the default values.
> Then iterate over the container to get the final policies?

Yep, something like that. For instance,

    namespace aux {
    typedef mpl::map<
          mpl::list<
              scalar_storage<_>
            , assert_check<_>
            , ref_counted<_>
            , disallow_conversion<_>
>
        , get_category<_> // "key" function
> smart_ptr_default_params;
    }

    template<
          typename DefaultParamsMap
        , typename Params
>
    struct params_map_gen
        : mpl::fold<
              Params
            , DefaultParamsMap
            , mpl::insert<_,_>
>
    {
    };

    template <
        typename T,
        class P1 = empty_policy, class P2 = empty_policy,
        class P3 = empty_policy, class P4 = empty_policy,
        class params_map_ = typename params_map_gen<
              aux::smart_ptr_default_params
            , mpl::list<P1,P2,P3,P4>
>::type
>
    class smart_ptr
        : public optimally_inherit<
            optimally_inherit<
                  typename aux::storage_policy<params_map_,T>::type
...

where 'storage_policy' is as simple as this:

    template< typename PolicyMap, typename T >
    struct storage_policy
    {
        typedef typename at<
              PolicyMap
            , storage_policy_tag
>::type f_;
            
        typedef typename apply_lambda<f_,T>::type type;
    };

> I might be able to hack a cheeseball metamap, though
> not a tree implementation (yet).

At this point I'm more interested in formulating an interface for
associative containers than their implementation qualities, so anything that
works will do.

Aleksey