$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
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