Ranges and primitive arrays and argument forwarding
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
Well, it wasn't as bad as I thought. First of all, I had munged the template, and didn't know that's where the errors were coming from. Corrected, I have 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. CharT* found= std::find (p, p+capacity, CharT(0)); return boost::iterator_range<CharT*> (p, found); } and then the main function works when declared with references: template <typename T> size_t Strlen (const T& s) { return internal::Strlen (internal::as_literal(s)); } This blew up originally, so I wrote it as pass-by-value. It turns out that I was getting a fit from the compiler when the interesting form _was_ being used. Without &&, I still hope to keep proliferation of arguments down to a minimum because input arguments can simply always be const, and output arguments never are. —John
participants (1)
-
John M. Dlugosz