Boost logo

Boost Users :

Subject: [Boost-users] Ranges and primitive arrays and argument forwarding
From: John M. Dlugosz (mpbecey7gu_at_[hidden])
Date: 2011-07-11 09:13:07


In a nutshell, I want to use ranges to unify several different ways of representing
"strings" in the program. This includes subranges of an existing sequence, so for
consistancy a "fixed" sized container does not need a nul termination but can fill the
container completely. The point here is that I want to treat arrays differently from
pointers, which is unlike boost::as_literal.

The idea is that I can apply a pre-conditioning function to any supported type, and get a
iterator_range back out. Here is an example of a trivial function to test it out, that
just returns the length:

     namespace internal {

     template <typename RangeT>
     size_t Strlen (RangeT s)
      {
      return boost::size(s);
      }

     }

     template <typename T>
     size_t Strlen (T s)
      {
      return internal::Strlen (internal::as_literal(s));
      }

For a more complex function, every argument that is meant to be a generalized "string"
will be so-wrapped, and pass on to the underlying internal form which expects that they
are all Ranges.

The conditioning needed is almost the same as boost::as_literal, so I copied and modified
that:

     template <typename CharT>
     inline boost::iterator_range<CharT> measure_fixed (CharT* p, std::size_t capacity)
      {
      // The deduced CharT will include the const if called on a const array.
      Char* found= std::find (p, p+capacity, CharT(0));
         // the find algorithm naturally does what I want, returning the same 'end' if not
found.
      return boost::iterator_range<CharT> (p, found);
      }

     // Copied directly from Boost: pointers and containers
     template< class Range >
     inline boost::iterator_range<typename boost::range_iterator<Range>::type>
     as_literal( Range& r )
      {
      return boost::range_detail::make_range( r, boost::range_detail::is_char_ptr(r) );
      }

     // Ditto, with const. Duplication needed because && not available on VS8
     template< class Range >
     inline boost::iterator_range<typename boost::range_iterator<const Range>::type>
     as_literal( const Range& r )
      {
      return boost::range_detail::make_range( r, boost::range_detail::is_char_ptr(r) );
      }

     // Actual arrays: I want to treat differently from pointers.
     template <typename Char, std::size_t sz >
     inline boost::iterator_range<Char*> as_literal( Char (&arr)[sz] )
      {
      return measure_fixed (arr, sz);
      }

     // Duplicate needed for const form.
     template< typename Char, std::size_t sz >
     inline boost::iterator_range<const Char*> as_literal( const Char (&arr)[sz] )
      {
      return measure_fixed (arr, sz);
      }

However, I find that it is NOT distinguishing arrays:
     char cA[4]= { 'a', 'b', 'c', 'd' }; // fixed size array, no termination
     size_t result= Strlen (cA);

This is calling the regular form, not the arragy form. The top-level function's call to
internal::as_literal is going to the first form, not the third. The parameter, r, is of
type char*& from what the debugger tells me, so the template argument Range must be
deduced as char*. The original Strlen see s as a char*, so it's already lost it at this
point.

If I have to write every top-level function to take separate array and non-array template
forms, besides being less elegant it will be a combinitational explosion on functions that
take multiple arguments.

Is there a way around this that work in Visual Studio 2005 (a.k.a. VS8)?
When the project finally moves to a newer compiler, does rvalue references ("perfect
forwarding") fix this issue, or is it still losing the arrayness by design?

Thanks,
—John


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