Gordon Henriksen
2009-May-15 17:52 UTC
[LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)
On 2009-05-15, at 07:26, David Greene wrote:> On Friday 15 May 2009 05:50, Jay Foad wrote: >> > >>> The one major thing to be aware of is that it isn't safe to use >>> &V[0] when V is an empty std::vector >> >> Oh dear. That's a bit of a flaw in the plan. I suppose the solution >> is to switch to SmallVector whenever this might be a problem. > > Or use iterators. That's why they're there.The reason to use the pointer-length pair in preference to iterators is that iterators make templates of everything, causing code bloat. In my code, rather than pass the separate parameters I use a range<Iter> class something like this: template<typename Iter> class range { Iter Begin, End; public: range() : Begin(), End() { } template<typename ConvertibleToRange> range(T &seq) : Begin(seq.begin()), End(seq.end()) { } Iter begin() { return Begin; } Iter end() { return End; } // ... other Sequence methods ... }; And encapsulate the logic to deal with the empty sequence problem in a function: template<typename ConvertibleToRange> range<typename ConvertibleToRange::value_type*> ptr_range(ConvertibleToRange &seq) { typename ConvertibleToRange::iterator i = seq.begin(), e = seq.end(); if (i == e) return range<typename ConvertibleToRange::value_type*>(); return range<typename ConvertibleToRange::value_type*>(&*i, &*i + (e - i)); }; So that: StructType::get(v.empty()? 0 : &v[0], v.empty()? 0 : &v[0] + v.size()); // becomes StructType::get(ptr_range(v)); Two important refinements… First, the above can be made safer so that ptr_range will reject e.g. std::list with a bit of metaprogramming: template<typename T> struct is_ptr { enum { value = false }; }; template<typename T> struct is_ptr<T*> { enum { value = true }; }; template<typename T> struct is_contiguous_sequence { enum { value = is_ptr<T::iterator>::value }; }; template<typename T, typename Alloc> struct is_contiguous_sequence<std::vector<T,Alloc> > { enum { value = true }; }; template<typename T, typename Ch, typename Alloc> struct is_contiguous_sequence<std::basic_string<T,Ch,Alloc> > { enum { value = true }; }; template<typename T, bool True> struct type_if { typedef T type; } template<typename T> struct type_if<T,false> { /* cause substitution failure */ } // New prototype for ptr_range. template<typename ConvertibleRange> typename type_if< range<typename T::value_type*>, is_contiguous_sequence<T>::value >::type ptr_range(ConvertibleRange &seq); And secondly, range<T*> can be extended to convert from arrays in addition to objects with begin()/end() methods by (1) providing a partial specialization on T* which adds a template constructor: template<typename T> template<size_t N> range<T*>::range<T*>(T (&arr)[N]) : Begin(arr), End(arr + N) { } or by (2a) using a traits type to look up ConvertibleToRange::iterator & -::value_type template<typename T> struct range_traits { typedef typename T::value_type value_type; typedef typename T::iterator iterator; // .. etc ... }; template<typename T, size_t N> struct range_traits<T[N]> { typedef T value_type; typedef T *iterator; // .. etc ... }; and by (2b) indirecting calls to seq.begin() and seq.end() through template functions: template<typename ConvertibleToRange> typename range_traits<T>::iterator beginof(T &seq) { seq.begin(); } template<typename ConvertibleToRange, size_t N> typename range_traits<T[N]>::iterator beginof(T (&seq)[N]) { return seq; } template<typename ConvertibleToRange> typename range_traits<T>::iterator endof(T &seq) { seq.begin(); } template<typename ConvertibleToRange, size_t N> typename range_traits<T[N]>::iterator endof(T (&seq)[N]) { return seq + N; } This refines StructType::get(arr, arr + sizeof(arr) / sizeof(arr[0])); to just StructType::get(arr); — Gordon
Chris Lattner
2009-May-15 18:17 UTC
[LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)
On May 15, 2009, at 10:52 AM, Gordon Henriksen wrote:> On 2009-05-15, at 07:26, David Greene wrote: > >> On Friday 15 May 2009 05:50, Jay Foad wrote: >>> >> >>>> The one major thing to be aware of is that it isn't safe to use >>>> &V[0] when V is an empty std::vector >>> >>> Oh dear. That's a bit of a flaw in the plan. I suppose the solution >>> is to switch to SmallVector whenever this might be a problem. >> >> Or use iterators. That's why they're there. > > The reason to use the pointer-length pair in preference to iterators > is that iterators make templates of everything, causing code bloat. In > my code, rather than pass the separate parameters I use a range<Iter> > class something like this:I tend to prefer ranges as well, but iterators don't cause bloat. Just have something like this: template <typename iter> ... create(iter start, iter end) { if (start != end) return create(&*start, end-start); return create(0,0); } which inlines away. This requires the iterators to be sequential in memory, which is perfectly reasonable IMO. -Chris
Talin
2009-May-16 00:21 UTC
[LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)
Chris Lattner wrote:> On May 15, 2009, at 10:52 AM, Gordon Henriksen wrote: > >> On 2009-05-15, at 07:26, David Greene wrote: >> >>> On Friday 15 May 2009 05:50, Jay Foad wrote: >>> >>>>> The one major thing to be aware of is that it isn't safe to use >>>>> &V[0] when V is an empty std::vector >>>>> >>>> Oh dear. That's a bit of a flaw in the plan. I suppose the solution >>>> is to switch to SmallVector whenever this might be a problem. >>>> >>> Or use iterators. That's why they're there. >>> >> The reason to use the pointer-length pair in preference to iterators >> is that iterators make templates of everything, causing code bloat. In >> my code, rather than pass the separate parameters I use a range<Iter> >> class something like this: >> > > I tend to prefer ranges as well, but iterators don't cause bloat. > Just have something like this: >I tend to prefer begin/end iterators (or first/last for subranges, following the naming conventions of STL) for things like this - if the argument types are declared correctly, you should be able to use std::vector, SmallVector, or just regular POD pointers. I tend to prefer iterators for the (admittedly minor) reason that converting from a SmallVector to a start+size pair involves taking size(), which requires a scaling operation, whereas converting to first/last is just a bitcast at most.> template <typename iter> > ... create(iter start, iter end) { > if (start != end) > return create(&*start, end-start); > return create(0,0); > } > > which inlines away. This requires the iterators to be sequential in > memory, which is perfectly reasonable IMO. > > -Chris > > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev > >
Apparently Analagous Threads
- [LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)
- [LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)
- [LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)
- [LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)
- [LLVMdev] Removing std::vector from APIs (was Re: Mutating the elements of a ConstantArray)