Boost logo

Boost Users :

From: Joaquín Mª López Muñoz (joaquin_at_[hidden])
Date: 2007-03-29 03:28:00


Scott Meyers ha escrito:

> Since set seems to be broken, I find myself wanting to implement this
> metafunction. It need work only for vectors:
>
> // erase all occurrences of T in Seq
> template<typename Seq, typename T> struct eraseVal;
>
> After several hours and tens or hundreds of thousands of lines of error messages
> from three compilers, I am unable to get it working.
>
> My attempt to do it the "right" way looks like this, and I must warn you in
> advance that it's not pretty, unless you like an approach that calls find three
> times with the same arguments:
>
> template<typename Seq, typename T>
> struct eraseVal
> : mpl::eval_if<
> boost::is_same<typename mpl::find<Seq, T>::type,
> typename mpl::end<Seq>::type>,
> typename mpl::identity<Seq>::type,
> eraseVal<typename mpl::erase<Seq,
> typename mpl::find<Seq,T>::type,
> typename mpl::next<typename mpl::find<Seq,T>::type>::type
> >::type,
> T>
> >
> {};
>
> My more procedural attempt looks better (to me), but it still doesn't compile:
>
> template<typename Seq, typename T>
> struct eraseVal {
> typedef typename mpl::find<Seq,T>::type iter;
> typedef typename mpl::eval_if<
> boost::is_same<iter,
> typename mpl::end<Seq>::type>,
> typename mpl::identity<Seq>::type,
> typename eraseVal<typename mpl::erase<Seq,
> iter,
> mpl::next<iter>::type
> >::type,
> T>::type
> > type;
> };
>
> God I miss iteration. And I really wish emacs would match angle brackets in C++
> mode, sigh.
>
> Can somebody please help me out?

Hello Scott, I see several problems with your procedural approach: the first one
is the ::type added to mpl::identity<Seq> and eraseVal<...> in the expression

    typedef typename mpl::eval_if<
      boost::is_same<...>,
      typename mpl::identity<Seq>::type,
      typename eraseVal<...>::type
> type;

Since eval_if is designed precisely to defer invocation of its argument
metafunctions the correct mode of use is

    typedef typename mpl::eval_if<
      boost::is_same<...>,
      typename mpl::identity<Seq>,
      typename eraseVal<...>
> type;

so that infinite recursion on eraseVal is avoided. But even after dropping those
::type's you still get errors like this:

[...]/boost/mpl/next_prior.hpp: In instantiation of `boost::mpl::next<mpl_::void_>':

[...]
[...]/boost/mpl/next_prior.hpp:31: no type named `next' in `struct mpl_::void_'

The problem now is that, although the eraseVal<...> expression is only invoked
(i.e. its nested ::type calculated) when strictly necessary --this is what eval_if
is
used for--, its first argument is computed *always*, regardless of what branch
of the eval_if<...> expr is finally selected:

  typename eraseVal<
    typename mpl::erase<Seq,iter,mpl::next<iter>::type>,
    T>

See the mpl::erase<...> thing? When no more T's are left in the sequence,
iter points to the end of Seq, and mpl::next<iter>::type is an *invalid*
expression (Incidentally, a ::type is missing for mpl::erase<...>, but this is not
the
root of the problem here.)

So, what we need if to add a layer of indirection so as to not invoke the
arguments to the recursive call to eraseVal except when strictly necessary,
like for instance as follows:

  template<typename Seq,typename T>
  struct eraseVal
  {
     template<typename Iter>
     struct eraseValRecurse:
       eraseVal<typename mpl::erase<Seq,Iter>::type,T>
     {};

     typedef typename mpl::find<Seq,T>::type iter;

     typedef typename mpl::eval_if<
       boost::is_same<iter,typename mpl::end<Seq>::type>,
       mpl::identity<Seq>,
       eraseValRecurse<iter>
>::type type;
  };

This works, but it is quite inefficient, as mpl::find, which computes
in linear time, is called for every element of Seq. The same thing
can be done in one pass through the sequence as follows:

template<typename Seq,typename T>
struct eraseVal2:mpl::copy_if<Seq,mpl::not_<boost::is_same<mpl::_,T> > >
{};

Please find attached a compilable snippet showing both algorithms
at work.

HTH,

Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo




Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net