PVData C++  8.0.6-dev
sharedVector.h
1 /* sharedVector.h */
2 /*
3  * Copyright information and license terms for this software can be
4  * found in the file LICENSE that is included with the distribution
5  */
6 #ifndef SHAREDVECTOR_H
7 #define SHAREDVECTOR_H
8 
9 #include <ostream>
10 #include <algorithm>
11 #include <stdexcept>
12 #include <iterator>
13 
14 #if __cplusplus>=201103L
15 # include <initializer_list>
16 #endif
17 
18 #include <cassert>
19 
20 #include "pv/sharedPtr.h"
21 #include "pv/pvIntrospect.h"
22 #include "pv/typeCast.h"
23 #include "pv/templateMeta.h"
24 
25 namespace epics { namespace pvData {
26 
27 template<typename E, class Enable = void> class shared_vector;
28 
29 template<typename TO, typename FROM>
30 static FORCE_INLINE
31 shared_vector<TO>
32 const_shared_vector_cast(shared_vector<FROM>& src);
33 
34 namespace detail {
35  template<typename E>
36  struct default_array_deleter {void operator()(E a){delete[] a;}};
37 
38  // How values should be passed as arguments to shared_vector methods
39  // really should use boost::call_traits
40  template<typename T> struct call_with { typedef T type; };
41  template<typename T> struct call_with<std::tr1::shared_ptr<T> >
42  { typedef const std::tr1::shared_ptr<T>& type; };
43  template<> struct call_with<std::string> { typedef const std::string& type; };
44 
48 
49  /* All the parts of shared_vector which
50  * don't need special handling for E=void
51  */
52  template<typename E>
54  {
55  // allow specialization for all E to be friends
56  template<typename E1> friend class shared_vector_base;
57  protected:
58  // NOTE: Do no use m_data, since VxWorks has a 'm_data' macro defined
59  std::tr1::shared_ptr<E> m_sdata;
60  //! Offset in the data array of first visible element
61  size_t m_offset;
62  //! Number of visible elements between m_offset and end of data
63  size_t m_count;
64  //! Total number of elements between m_offset and the end of data
65  size_t m_total;
66 
67  /* invariants
68  * m_count <= m_total (enforced)
69  * m_offset + m_total <= (size_t)-1 (checked w/ assert())
70  */
71 
72  public:
73 #if __cplusplus>=201103L
74  //! @brief Empty vector (not very interesting)
75  constexpr shared_vector_base() noexcept
76  :m_sdata(), m_offset(0), m_count(0), m_total(0)
77  {}
78 #else
79  //! @brief Empty vector (not very interesting)
81  :m_sdata(), m_offset(0), m_count(0), m_total(0)
82  {}
83 #endif
84 
85  protected:
86  // helper for constructors
87  // Ensure that offset and size are zero when we are constructed with NULL
88  void _null_input()
89  {
90  if(!m_sdata) {
91  m_offset = m_total = m_count = 0;
92  } else {
93  // ensure we won't have integer overflows later
94  assert( m_offset <= ((size_t)-1) - m_total);
95  }
96  }
97  public:
98 
99  template<typename A>
100  shared_vector_base(A* v, size_t o, size_t c)
101  :m_sdata(v, detail::default_array_deleter<A*>())
102  ,m_offset(o), m_count(c), m_total(c)
103  {_null_input();}
104 
105  shared_vector_base(const std::tr1::shared_ptr<E>& d, size_t o, size_t c)
106  :m_sdata(d), m_offset(o), m_count(c), m_total(c)
107  {_null_input();}
108 
109 
110  template<typename A, typename B>
111  shared_vector_base(A d, B b, size_t o, size_t c)
112  :m_sdata(d,b), m_offset(o), m_count(c), m_total(c)
113  {_null_input();}
114 
115  shared_vector_base(const shared_vector_base& O)
116  :m_sdata(O.m_sdata), m_offset(O.m_offset)
117  ,m_count(O.m_count), m_total(O.m_total)
118  {}
119 
120 #if __cplusplus >= 201103L
121  shared_vector_base(shared_vector_base &&O)
122  :m_sdata(std::move(O.m_sdata))
123  ,m_offset(O.m_offset)
124  ,m_count(O.m_count)
125  ,m_total(O.m_total)
126  {
127  O.clear();
128  }
129 #endif
130 
131  protected:
132  typedef typename meta::strip_const<E>::type _E_non_const;
133  public:
134  //! Constructor used to implement freeze().
135  //! Should not be called directly.
136  shared_vector_base(shared_vector_base<_E_non_const>& O,
138  :m_sdata()
140  ,m_count(O.m_count)
141  ,m_total(O.m_total)
142  {
143  if(!O.unique())
144  throw std::runtime_error("Can't freeze non-unique vector");
145 #if __cplusplus >= 201103L
146  m_sdata = std::move(O.m_sdata);
147 #else
148  m_sdata = O.m_sdata;
149 #endif
150  O.clear();
151  }
152 
153  //! Constructor used to implement thaw().
154  //! Should not be called directly.
155  shared_vector_base(shared_vector<const E>& O,
157  :m_sdata()
159  ,m_count(O.m_count)
160  ,m_total(O.m_total)
161  {
162  O.make_unique();
163 #if __cplusplus >= 201103L
164  m_sdata = std::move(std::tr1::const_pointer_cast<E>(O.m_sdata));
165 #else
166  m_sdata = std::tr1::const_pointer_cast<E>(O.m_sdata);
167 #endif
168  O.clear();
169  }
170 
171  //! @brief Copy an existing vector
173  {
174  if(&o!=this) {
175  m_sdata=o.m_sdata;
176  m_offset=o.m_offset;
177  m_count=o.m_count;
178  m_total=o.m_total;
179  }
180  return *this;
181  }
182 
183 #if __cplusplus >= 201103L
184  //! @brief Move an existing vector
185  shared_vector_base& operator=(shared_vector_base&& o)
186  {
187  if(&o!=this) {
188  m_sdata=std::move(o.m_sdata);
189  m_offset=o.m_offset;
190  m_count=o.m_count;
191  m_total=o.m_total;
192  o.clear();
193  }
194  return *this;
195  }
196 #endif
197 
198  //! @brief Swap the contents of this vector with another
200  if(&o!=this) {
201  m_sdata.swap(o.m_sdata);
202  std::swap(m_count, o.m_count);
203  std::swap(m_offset, o.m_offset);
204  std::swap(m_total, o.m_total);
205  }
206  }
207 
208  //! @brief Clear contents.
209  //! size() becomes 0
210  void clear() {
211  m_sdata.reset();
212  m_offset = m_total = m_count = 0;
213  }
214 
215  //! @brief Data is not shared?
216  bool unique() const {return !m_sdata || m_sdata.use_count()<=1;}
217 
218 
219  //! @brief Number of elements visible through this vector
220  size_t size() const{return m_count;}
221  //! @brief shorthand for size()==0
222  bool empty() const{return !m_count;}
223 
224 
225  /** @brief Reduce the view of this shared_vector.
226  *
227  * Reduce the portion of the underlying buffer which
228  * is accessible through this shared_vector.
229  *
230  * When the requested new offset and length are not possible
231  * then the following holds.
232  *
233  * When offset is >= size() then after slice() size()==0.
234  * When length >= size()-offset then after slice()
235  * size() = old_size-offset.
236  *
237  @param offset The request new offset relative to the
238  * current offset.
239  @param length The requested new length.
240  *
241  @note offset and length are in units of sizeof(E).
242  * or bytes (1) when E=void.
243  */
244  void slice(size_t offset, size_t length=(size_t)-1)
245  {
246  if(offset>m_count)
247  offset = m_count; // will slice down to zero length
248 
249  const size_t max_count = m_count - offset;
250 
251  m_offset += offset;
252 
253  m_total -= offset;
254 
255  if(length > max_count)
256  length = max_count;
257  m_count = length;
258  }
259 
260  // Access to members.
261  const std::tr1::shared_ptr<E>& dataPtr() const { return m_sdata; }
262  size_t dataOffset() const { return m_offset; }
263  size_t dataCount() const { return m_count; }
264  size_t dataTotal() const { return m_total; }
265  };
266 }
267 
268 /** @brief A holder for a contiguous piece of memory.
269  *
270  * Data is shared, but offset and length are not.
271  * This allows one vector to have access to only a
272  * subset of a piece of memory.
273  *
274  * The ways in which shared_vector is intended to differ from
275  * std::vector are outlined in @ref vectordiff .
276  *
277  * Also see @ref vectormem and @ref vectorconst
278  *
279  * @warning Due to the implementation of std::tr1::shared_ptr, use of
280  * shared_vector should not be combined with use of weak_ptr.
281  * shared_ptr::unique() and shared_ptr::use_count() do @b not
282  * include weak_ptr instances. This breaks the assumption made
283  * by make_unique() that unique()==true implies exclusive
284  * ownership.
285  */
286 template<typename E, class Enable>
287 class shared_vector : public detail::shared_vector_base<E>
288 {
289  typedef detail::shared_vector_base<E> base_t;
290  typedef typename detail::call_with<E>::type param_type;
291  typedef typename meta::strip_const<E>::type _E_non_const;
292 public:
293  typedef E value_type;
294  typedef E& reference;
295  typedef typename meta::decorate_const<E>::type& const_reference;
296  typedef E* pointer;
297  typedef typename meta::decorate_const<E>::type* const_pointer;
298  typedef E* iterator;
299  typedef std::reverse_iterator<iterator> reverse_iterator;
300  typedef typename meta::decorate_const<E>::type* const_iterator;
301  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
302  typedef ptrdiff_t difference_type;
303  typedef size_t size_type;
304 
305  typedef E element_type;
306  typedef std::tr1::shared_ptr<E> shared_pointer_type;
307 
308  // allow specialization for all E to be friends
309  template<typename E1, class Enable1> friend class shared_vector;
310 
311  //! @brief Empty vector (not very interesting)
312 #if __cplusplus>=201103L
313  constexpr shared_vector() noexcept :base_t() {}
314 #else
316 #endif
317 
318 #if __cplusplus>=201103L
319  template<typename A>
320  shared_vector(std::initializer_list<A> L)
321  :base_t(new _E_non_const[L.size()], 0, L.size())
322  {
323  _E_non_const *raw = const_cast<_E_non_const*>(data());
324  std::copy(L.begin(), L.end(), raw);
325  }
326 #endif
327 
328  //! @brief Allocate (with new[]) a new vector of size c
329  explicit shared_vector(size_t c)
330  :base_t(new _E_non_const[c], 0, c)
331  {}
332 
333  //! @brief Allocate (with new[]) a new vector of size c and fill with value e
334  shared_vector(size_t c, param_type e)
335  :base_t(new _E_non_const[c], 0, c)
336  {
337  std::fill_n((_E_non_const*)this->m_sdata.get(), this->m_count, e);
338  }
339 
340  /** @brief Build vector from a raw pointer
341  *
342  @param v A raw pointer allocated with new[].
343  @param o The offset in v or the first element visible to the vector
344  @param c The number of elements pointed to by v+o
345  */
346  template<typename A>
347  shared_vector(A v, size_t o, size_t c) :base_t(v,o,c) {}
348 
349  /** @brief Build vector from an existing smart pointer
350  *
351  @param d An existing smart pointer
352  @param o The offset in v or the first element visible to the vector
353  @param c The number of elements pointed to by v+o
354  */
355  template<typename E1>
356  shared_vector(const std::tr1::shared_ptr<E1>& d, size_t o, size_t c)
357  :base_t(d,o,c) {}
358 
359  /** @brief Build vector from raw pointer and cleanup function
360  *
361  @param d An existing raw pointer
362  @param b An function/functor used to free d. Invoked as b(d).
363  @param o The offset in v or the first element visible to the vector
364  @param c The number of elements pointed to by v+o
365  */
366  template<typename A, typename B>
367  shared_vector(A d, B b, size_t o, size_t c)
368  :base_t(d,b,o,c) {}
369 
370  //! @brief Copy an existing vector of same type
371  shared_vector(const shared_vector& o) :base_t(o) {}
372 
373 #if __cplusplus>=201103L
374  //! @brief Move an existing vector of same type
375  shared_vector(shared_vector&& o) :base_t(std::move(o)) {}
376 #endif
377 
378  //! @internal
379  //! Internal for static_shared_vector_cast
380  template<typename FROM>
381  shared_vector(const shared_vector<FROM> &src,
382  detail::_shared_vector_cast_tag)
383  :base_t(std::tr1::static_pointer_cast<E>(src.dataPtr()),
384  src.dataOffset()/sizeof(E),
385  src.dataCount()/sizeof(E))
386  {}
387 
388 
389  shared_vector(shared_vector<typename base_t::_E_non_const>& O,
390  detail::_shared_vector_freeze_tag t)
391  :base_t(O,t)
392  {}
393 
394  shared_vector(shared_vector<const E>& O,
395  detail::_shared_vector_thaw_tag t)
396  :base_t(O,t)
397  {}
398 
399  inline shared_vector& operator=(const shared_vector& o)
400  {
401  this->base_t::operator=(o);
402  return *this;
403  }
404 
405 #if __cplusplus>=201103L
406  inline shared_vector& operator=(shared_vector&& o)
407  {
408  this->base_t::operator=(std::move(o));
409  return *this;
410  }
411 #endif
412 
413  size_t max_size() const{return ((size_t)-1)/sizeof(E);}
414 
415  size_t capacity() const { return this->m_total; }
416 
417  /** @brief Set array capacity
418  *
419  * A side effect is that array data will be uniquely owned by this instance
420  * as if make_unique() was called. This holds even if the capacity
421  * does not increase.
422  *
423  * For notes on copying see docs for make_unique().
424  *
425  * @throws std::bad_alloc if requested allocation can not be made
426  * @throws other exceptions from element copy ctor
427  */
428  void reserve(size_t i) {
429  if(this->unique() && i<=this->m_total)
430  return;
431  size_t new_count = this->m_count;
432  if(new_count > i)
433  new_count = i;
434  _E_non_const* temp=new _E_non_const[i];
435  try{
436  std::copy(begin(), begin()+new_count, temp);
437  }catch(...){
438  delete[] temp;
439  throw;
440  }
441  this->m_sdata.reset(temp, detail::default_array_deleter<E*>());
442  this->m_offset = 0;
443  this->m_count = new_count;
444  this->m_total = i;
445  }
446 
447  /** @brief Grow or shrink array
448  *
449  * A side effect is that array data will be uniquely owned by this instance
450  * as if make_unique() were called. This holds even if the size does not change.
451  *
452  * For notes on copying see docs for make_unique().
453  *
454  * @throws std::bad_alloc if requested allocation can not be made
455  * @throws other exceptions from element copy ctor
456  */
457  void resize(size_t i) {
458  if(i==this->m_count) {
460  return;
461  }
462  if(this->m_sdata && this->m_sdata.use_count()==1) {
463  // we have data and exclusive ownership of it
464  if(i<=this->m_total) {
465  // We have room to grow (or shrink)!
466  this->m_count = i;
467  return;
468  }
469  }
470  // must re-allocate :(
471  size_t new_total = this->m_total;
472  if(new_total < i)
473  new_total = i;
474  _E_non_const* temp=new _E_non_const[new_total];
475  try{
476  size_t n = this->size();
477  if(n > i)
478  n = i;
479  // Copy as much as possible from old,
480  // remaining elements are uninitialized.
481  std::copy(begin(),
482  begin()+n,
483  temp);
484  }catch(...){
485  delete[] temp;
486  throw;
487  }
488  this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
489  this->m_offset= 0;
490  this->m_count = i;
491  this->m_total = new_total;
492  }
493 
494  /** @brief Grow (and fill) or shrink array.
495  *
496  * see @ref resize(size_t)
497  */
498  void resize(size_t i, param_type v) {
499  size_t oldsize=this->size();
500  resize(i);
501  if(this->size()>oldsize) {
502  std::fill(begin()+oldsize, end(), v);
503  }
504  }
505 
506  /** @brief Ensure (by copying) that this shared_vector is the sole
507  * owner of the data array.
508  *
509  * If a copy is needed, memory is allocated with new[]. If this is
510  * not desirable then do something like the following.
511  @code
512  shared_vector<E> original(...);
513 
514  if(!original.unique()){
515  std::tr1::shared_ptr<E> sptr(myalloc(original.size()), myfree);
516  shared_vector<E> temp(sptr, 0, original.size());
517  std::copy(original.begin(), original.end(), temp.begin());
518  original.swap(temp);
519  }
520  assert(original.unique());
521  @endcode
522  *
523  * @throws std::bad_alloc if requested allocation can not be made
524  * @throws other exceptions from element copy ctor
525  */
526  void make_unique() {
527  if(this->unique())
528  return;
529  // at this point we know that !!m_sdata, so get()!=NULL
530  _E_non_const *d = new _E_non_const[this->m_total];
531  try {
532  std::copy(this->m_sdata.get()+this->m_offset,
533  this->m_sdata.get()+this->m_offset+this->m_count,
534  d);
535  }catch(...){
536  delete[] d;
537  throw;
538  }
539  this->m_sdata.reset(d, detail::default_array_deleter<E*>());
540  this->m_offset=0;
541  }
542 
543 private:
544  /* Hack alert.
545  * For reasons of simplicity and efficiency, we want to use raw pointers for iteration.
546  * However, shared_ptr::get() isn't defined when !m_sdata, although practically it gives NULL.
547  * Unfortunately, many of the MSVC (<= VS 2013) STL methods assert() that iterators are never NULL.
548  * So we fudge here by abusing 'this' so that our iterators are always !NULL.
549  */
550  inline E* base_ptr() const {
551 #if defined(_MSC_VER) && _MSC_VER<=1800
552  return this->m_count ? this->m_sdata.get() : (E*)(this-1);
553 #else
554  return this->m_sdata.get();
555 #endif
556  }
557 public:
558  // STL iterators
559 
560  iterator begin() const{return this->base_ptr()+this->m_offset;}
561  const_iterator cbegin() const{return begin();}
562 
563  iterator end() const{return this->base_ptr()+this->m_offset+this->m_count;}
564  const_iterator cend() const{return end();}
565 
566  reverse_iterator rbegin() const{return reverse_iterator(end());}
567  const_reverse_iterator crbegin() const{return rbegin();}
568 
569  reverse_iterator rend() const{return reverse_iterator(begin());}
570  const_reverse_iterator crend() const{return rend();}
571 
572  reference front() const{return (*this)[0];}
573  reference back() const{return (*this)[this->m_count-1];}
574 
575  // Modifications
576 
577 private:
578  void _push_resize() {
579  if(this->m_count==this->m_total || !this->unique()) {
580  size_t next;
581  if(this->m_total<1024) {
582  // round m_total+1 up to the next power of 2
583  next = this->m_total;
584  next |= next >> 1;
585  next |= next >> 2;
586  next |= next >> 4;
587  next |= next >> 8;
588  next++;
589  } else {
590  // pad m_total up to the next multiple of 1024
591  next = this->m_total+1024;
592  next &= ~0x3ff;
593  }
594  assert(next > this->m_total);
595  reserve(next);
596  }
597  resize(this->size()+1);
598  }
599 
600 public:
601 
602  void push_back(param_type v)
603  {
604  _push_resize();
605  back() = v;
606  }
607 
608  void pop_back()
609  {
610  this->slice(0, this->size()-1);
611  }
612 
613  // data access
614 
615  //! @brief Return Base pointer
616  pointer data() const{return this->m_sdata.get()+this->m_offset;}
617 
618  //! @brief Member access
619  //! Undefined if empty()==true.
620  reference operator[](size_t i) const {return this->m_sdata.get()[this->m_offset+i];}
621 
622  //! @brief Member access
623  //! @throws std::out_of_range if i>=size().
624  reference at(size_t i) const
625  {
626  if(i>this->m_count)
627  throw std::out_of_range("Index out of bounds");
628  return (*this)[i];
629  }
630 
631 };
632 
633 /**
634  * @brief Specialization for storing untyped pointers.
635  *
636  * Does not allow access or iteration of contents
637  * other than as void* or const void*
638  *
639  * In order to support shared_vector_convert<>()
640  * information about the type of the underlying allocation
641  * is stored.
642  * This is implicitly set by static_shared_vector_cast<>()
643  * and may be explicitly checked/changed using
644  * original_type()/set_original_type().
645  *
646  * A shared_vector<void> directly constructed
647  * from a smart pointer does not have an associated
648  * original_type().
649  * Use epics::pvData::ScalarTypeFunc::allocArray()
650  * to convienently allocate an array with a known
651  * original_type().
652  */
653 template<typename E>
654 class shared_vector<E, typename meta::is_void<E>::type >
655  : public detail::shared_vector_base<E>
656 {
657  typedef detail::shared_vector_base<E> base_t;
659 
660  // allow specialization for all E to be friends
661  template<typename E1, class Enable1> friend class shared_vector;
662 public:
663  typedef E value_type;
664  typedef E* pointer;
665  typedef ptrdiff_t difference_type;
666  typedef size_t size_type;
667 
669 
670 #if __cplusplus>=201103L
671  constexpr shared_vector() noexcept :base_t(), m_vtype((ScalarType)-1) {}
672 #else
673  shared_vector() :base_t(), m_vtype((ScalarType)-1) {}
674 #endif
675 
677  :base_t(v,o,c), m_vtype((ScalarType)-1) {}
678 
679  template<typename B>
681  :base_t(d,b,o,c), m_vtype((ScalarType)-1) {}
682 
683  template<typename E1>
685  :base_t(d,o,c), m_vtype((ScalarType)-1) {}
686 
688  :base_t(o), m_vtype(o.m_vtype) {}
689 
690 #if __cplusplus>=201103L
692  :base_t(std::move(o)), m_vtype(o.m_vtype) {}
693 #endif
694 
695  //! @internal
696  //! Internal for static_shared_vector_cast
697  template<typename FROM>
701  src.dataOffset()*sizeof(FROM),
702  src.dataCount()*sizeof(FROM))
704  {}
705 
708  :base_t(O,t), m_vtype(O.m_vtype)
709  {}
710 
711  shared_vector(shared_vector<const void>& O,
713  :base_t(O,t), m_vtype(O.m_vtype)
714  {}
715 
717  {
718  if(&o!=this) {
719  this->base_t::operator=(o);
720  m_vtype = o.m_vtype;
721  }
722  return *this;
723  }
724 
725 #if __cplusplus>=201103L
727  {
728  if(&o!=this) {
729  this->base_t::operator=(std::move(o));
730  m_vtype = o.m_vtype;
731  }
732  return *this;
733  }
734 #endif
735 
736  void swap(shared_vector& o) {
737  base_t::swap(o);
738  std::swap(m_vtype, o.m_vtype);
739  }
740 
741  size_t max_size() const{return (size_t)-1;}
742 
743  pointer data() const{
744  return (pointer)(((char*)this->m_sdata.get())+this->m_offset);
745  }
746 
747  shared_vector& set_original_type(ScalarType t) { m_vtype=t; return *this; }
748  ScalarType original_type() const {return m_vtype;}
749 };
750 
751 namespace detail {
752  template<typename TO, typename FROM, class Enable = void>
753  struct static_shared_vector_caster { /* no default */ };
754  // from void to non-void with same const-ness
755  template<typename TO, typename FROM>
756  struct static_shared_vector_caster<TO, FROM,
757  typename meta::_and<meta::_and<meta::is_not_void<TO>, meta::is_void<FROM> >,
758  meta::same_const<TO,FROM> >::type> {
759  static inline shared_vector<TO> op(const shared_vector<FROM>& src) {
760  return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
761  }
762  };
763  // from non-void to void with same const-ness
764  template<typename TO, typename FROM>
765  struct static_shared_vector_caster<TO, FROM,
766  typename meta::_and<meta::_and<meta::is_void<TO>, meta::is_not_void<FROM> >,
767  meta::same_const<TO,FROM> >::type> {
768  static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
769  return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
770  }
771  };
772 
773  // cast to same type, no-op
774  template<typename TOFRO>
775  struct static_shared_vector_caster<TOFRO,TOFRO,void> {
776  static FORCE_INLINE const shared_vector<TOFRO>& op(const shared_vector<TOFRO>& src) {
777  return src;
778  }
779  };
780 } // namespace detail
781 
782 /** @brief Allow casting of shared_vector between types
783  *
784  * Currently only to/from void is implemented.
785  *
786  @warning Casting from void is undefined unless the offset and length
787  * are integer multiples of the size of the destination type.
788  */
789 template<typename TO, typename FROM>
790 static FORCE_INLINE
791 shared_vector<TO>
792 static_shared_vector_cast(const shared_vector<FROM>& src)
793 {
794  return detail::static_shared_vector_caster<TO,FROM>::op(src);
795 }
796 
797 namespace detail {
798 
799  // Default to type conversion using castUnsafe (C++ type casting) on each element
800  template<typename TO, typename FROM, class Enable = void>
802  static inline shared_vector<TO> op(const shared_vector<FROM>& src)
803  {
804  shared_vector<TO> ret(src.size());
805  std::transform(src.begin(), src.end(), ret.begin(), castUnsafe<TO,FROM>);
806  return ret;
807  }
808  };
809 
810  // copy reference when types are the same (excluding const qualifiers)
811  template<typename TO, typename FROM>
812  struct shared_vector_converter<TO,FROM, typename meta::same_root<TO,FROM>::type > {
813  static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
814  return src;
815  }
816  };
817 
818  // "convert" to 'void' or 'const void from non-void
819  // is an alias for shared_vector_cast<void>()
820  template<typename TO, typename FROM>
821  struct shared_vector_converter<TO,FROM,
822  typename meta::_and<meta::is_void<TO>, meta::is_not_void<FROM> >::type
823  >
824  {
825  static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
826  return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
827  }
828  };
829 
830  // convert from void uses original type or throws an exception.
831  template<typename TO, typename FROM>
832  struct shared_vector_converter<TO,FROM,
833  typename meta::_and<meta::is_not_void<TO>, meta::is_void<FROM> >::type
834  >
835  {
836  static shared_vector<TO> op(const shared_vector<FROM>& src) {
837  typedef typename meta::strip_const<TO>::type to_t;
838  ScalarType stype = src.original_type(),
839  dtype = (ScalarType)ScalarTypeID<TO>::value;
840  if(src.empty()) {
841  return shared_vector<TO>();
842 
843  } else if(stype==dtype) {
844  // no convert needed
845  return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
846 
847  } else {
848  // alloc and convert
849  shared_vector<to_t> ret(src.size()/ScalarTypeFunc::elementSize(stype));
850  castUnsafeV(ret.size(),
851  dtype,
852  static_cast<void*>(ret.data()),
853  stype,
854  static_cast<const void*>(src.data()));
855  return const_shared_vector_cast<TO>(ret);
856  }
857  }
858  };
859 }
860 
861 /** @brief Allow converting of shared_vector between types
862  *
863  * Conversion utilizes castUnsafe<TO,FROM>().
864  *
865  * Converting to/from void is supported. Convert to void
866  * is an alias for static_shared_vector_cast<void>().
867  * Convert from void utilizes shared_vector<void>::original_type().
868  *
869  * @throws std::runtime_error if cast is not valid.
870  * @throws std::bad_alloc for out of memory condition
871  */
872 template<typename TO, typename FROM>
873 static FORCE_INLINE
874 shared_vector<TO>
875 shared_vector_convert(const shared_vector<FROM>& src)
876 {
877  return detail::shared_vector_converter<TO,FROM>::op(src);
878 }
879 
880 /** @brief transform a shared_vector<T> to shared_vector<const T>
881  *
882  * Transform a reference to mutable data into a reference to read-only data.
883  * Throws an exception unless the reference to mutable data is unique.
884  * On success the reference to mutable data is cleared.
885  */
886 template<typename SRC>
887 static FORCE_INLINE
888 shared_vector<typename meta::decorate_const<typename SRC::value_type>::type>
889 freeze(SRC& src)
890 {
891  typedef typename meta::decorate_const<typename SRC::value_type>::type const_value;
892  return shared_vector<const_value>(src, detail::_shared_vector_freeze_tag());
893 }
894 
895 /** @brief transform a shared_vector<const T> to shared_vector<T>
896  *
897  * Transform a reference to read-only data into a unique reference to mutable data.
898  *
899  * The reference to read-only data is cleared.
900  */
901 template<typename SRC>
902 static FORCE_INLINE
903 shared_vector<typename meta::strip_const<typename SRC::value_type>::type>
904 thaw(SRC& src)
905 {
906  typedef typename meta::strip_const<typename SRC::value_type>::type value;
907  return shared_vector<value>(src, detail::_shared_vector_thaw_tag());
908 }
909 
910 namespace detail {
911  template<typename TO, typename FROM, class Enable = void>
912  struct const_caster {};
913 
914  template<typename TYPE>
915  struct const_caster<TYPE,const TYPE> {
916  static FORCE_INLINE shared_vector<TYPE> op(shared_vector<const TYPE>& src)
917  {
918  return thaw(src);
919  }
920  };
921 
922  template<typename TYPE>
923  struct const_caster<const TYPE,TYPE> {
924  static FORCE_INLINE shared_vector<const TYPE> op(shared_vector<TYPE>& src)
925  {
926  return freeze(src);
927  }
928  };
929 
930  template<typename TYPE>
931  struct const_caster<TYPE,TYPE> {
932  static FORCE_INLINE shared_vector<TYPE> op(shared_vector<TYPE>& src)
933  {
934  shared_vector<TYPE> ret(src);
935  src.clear();
936  return ret;
937  }
938  };
939 }
940 
941 //! Allows casting from const TYPE -> TYPE.
942 template<typename TO, typename FROM>
943 static FORCE_INLINE
944 shared_vector<TO>
945 const_shared_vector_cast(shared_vector<FROM>& src)
946 {
947  return detail::const_caster<TO,FROM>::op(src);
948 }
949 
950 
951 namespace ScalarTypeFunc {
952  //! Allocate an untyped array based on ScalarType
954 
955  //! Allocate an untyped array based on ScalarType
956  template<ScalarType ID>
957  inline
960  {
961  shared_vector<void> raw(allocArray(ID, len));
962  return static_shared_vector_cast<typename ScalarTypeTraits<ID>::type>(raw);
963  }
964 }
965 
966 }} // namespace epics::pvData
967 
968 // Global operators for shared_vector
969 
970 template<typename A, typename B>
971 bool operator==(const epics::pvData::shared_vector<A>& a,
972  const epics::pvData::shared_vector<B>& b)
973 {
974  if(a.size() != b.size())
975  return false;
976  if(a.dataOffset()==b.dataOffset() && a.dataPtr().get()==b.dataPtr().get())
977  return true;
978  return std::equal(a.begin(), a.end(), b.begin());
979 }
980 
981 template<typename A, typename B>
982 bool operator!=(const epics::pvData::shared_vector<A>& a,
983  const epics::pvData::shared_vector<B>& b)
984 {
985  return !(a==b);
986 }
987 
988 template<typename E>
989 std::ostream& operator<<(std::ostream& strm, const epics::pvData::shared_vector<E>& arr)
990 {
991  strm<<'{'<<arr.size()<<"}[";
992  for(size_t i=0; i<arr.size(); i++) {
993  if(i>10) {
994  strm<<"...";
995  break;
996  }
997  strm<<arr[i];
998  if(i+1<arr.size())
999  strm<<", ";
1000  }
1001  strm<<']';
1002  return strm;
1003 }
1004 
1005 
1006 #endif // SHAREDVECTOR_H
1007 
1008 /** @page vectordiff Differences between std::vector and shared_vector
1009  *
1010  * @section diffbehave Differences in behavior
1011  *
1012  * shared_vector models const-ness like shared_ptr. A equivalent of
1013  * 'const std::vector<E>' is 'const shared_vector<const E>'. However,
1014  * it is also possible to have 'const shared_vector<E>' analogous to
1015  * 'E* const' and 'shared_vector<const E>' which is analogous to
1016  * 'const E*'.
1017  *
1018  * Copying a shared_vector, by construction or assignment, does
1019  * not copy its contents. Modifications to one such "copy" effect
1020  * all associated shared_vector instances.
1021  *
1022  * std::vector::reserve(N) has no effect if N<=std::vector::capacity().
1023  * However, like resize(), shared_vector<E>::reserve() has the side
1024  * effect of always calling make_unique().
1025  *
1026  * @section notimpl Parts of std::vector interface not implemented
1027  *
1028  * Mutating methods insert(), erase(), shrink_to_fit(),
1029  * emplace(), and emplace_back() are not implemented.
1030  *
1031  * shared_vector does not model an allocator which is bound to the object.
1032  * Therefore the get_allocator() method and the allocator_type typedef are
1033  * not provided.
1034  *
1035  * The assign() method and the related constructor are not implemented
1036  * at this time.
1037  *
1038  * The comparison operators '>', '>=', '<=', and '<' are not implemented
1039  * at this time.
1040  *
1041  * @section newstuff Parts not found in std::vector
1042  *
1043  * shared_vector has additional constructors from raw pointers
1044  * and shared_ptr s.
1045  *
1046  * Implicit casting is not allowed. Instead use
1047  * const_shared_vector_cast()/freeze()/thaw() (@ref vectorconst)
1048  * to casting between 'T' and 'const T'.
1049  * Use static_shared_vector_cast() to cast between
1050  * void and non-void (same const-ness).
1051  *
1052  * To facilitate safe modification the methods unique() and
1053  * make_unique() are provided.
1054  *
1055  * The slice() method selects a sub-set of the shared_vector.
1056  *
1057  * The low level accessors dataPtr(), dataOffset(), dataCount(),
1058  * and dataTotal().
1059  *
1060  */
1061 
1062 /** @page vectormem Memory Management with shared_vector
1063  *
1064  * The @link epics::pvData::shared_vector shared_vector class @endlink
1065  * is a std::vector like class which implements sharing data by reference counting.
1066  *
1067  * Internally memory is tracked with the shared_ptr reference counting smart pointer.
1068  * This allows a custom destructor to be specified. This allows a vector to borrow
1069  * memory allocated by 3rd party libraries which require special cleanup.
1070  *
1071  * In place element modification is allowed. It is left to user code to ensure
1072  * that such modification is safe, either from application specific knowledge, or by
1073  * calling
1074  * @link paramTable::string_data::make_unique make_unique @endlink
1075  * explicitly, or implicitly by calling
1076  * @link paramTable::shared_vector::resize resize @endlink
1077  * prior to making modifications.
1078  *
1079  @code
1080  extern "C" {
1081  // array embedded in C structure
1082  struct composite {
1083  int other, stuff;
1084  char buf[42];
1085  }
1086 
1087  // Unknown relation between array and handle
1088  typedef void* handle_type;
1089  handle_type mylib_alloc(void);
1090  char *mylib_mem(handle_type);
1091  void mylib_free(handle_type);
1092  }
1093 
1094  // Note that mylibcleaner must be copy constructable
1095  struct mylibcleaner {
1096  handle_type handle;
1097  mylibcleaner(handle_type h) :handle(h) {}
1098  void operator()(char*){ mylib_free(handle);}
1099  };
1100 
1101  struct compcleaner {
1102  void operator()(char* c){ free(c-offsetof(composite,buf)); }
1103  };
1104 
1105  void main() {
1106 
1107  unsigned char* buf=calloc(42,1);
1108 
1109  shared_vector<epicsUInt8> a(buf, &free);
1110 
1111  a.clear(); // calls free(ptr)
1112 
1113 
1114  composite *c=malloc(sizeof(*c));
1115  assert(c!=NULL);
1116 
1117  shared_vector<char> d(c->buf, compcleaner());
1118 
1119  d.clear(); // calls free(ptr-offsetof(composite,buf))
1120 
1121 
1122  void *handle=mylib_alloc();
1123  char *hmem=mylib_mem(handle);
1124  assert(hmem!=NULL);
1125 
1126  shared_vector<epicsUInt8> b(hmem, mylibcleaner(handle));
1127 
1128  b.clear(); // calls mylib_free(handleptr)
1129  }
1130  @endcode
1131  */
1132 
1133 /** @page vectorconst Value const-ness and shared_vector
1134 
1135 The type 'shared_vector<T>' can be thought of as 'T*'.
1136 Like the T pointer there are three related constant types:
1137 
1138 @code
1139  shared_vector<int> v_mutable; // 1
1140  const shared_vector<int> v_const_ref; // 2
1141  shared_vector<const int> v_const_data; // 3
1142  const shared_vector<const int> v_const_ref_data; // 4
1143 @endcode
1144 
1145 The distinction between these types is what "part" of the type is constant,
1146 the "reference" (pointer) or the "value" (location being pointed to).
1147 
1148 Type #2 is constant reference to a mutable value.
1149 Type #3 is a mutable reference to a constant value.
1150 Type #4 is a constant reference to a constant value.
1151 
1152 Casting between const and non-const values does @b not follow the normal
1153 C++ casting rules (no implicit cast).
1154 
1155 For casting between shared_vector<T> and shared_vector<const T>
1156 explicit casting operations are required. These operations are
1157 @b freeze() (non-const to const) and @b thaw() (const to non-const).
1158 
1159 A 'shared_vector<const T>' is "frozen" as its value can not be modified.
1160 However it can still be sliced because the reference is not const.
1161 
1162 These functions are defined like:
1163 
1164 @code
1165 namespace epics{namespace pvData{
1166  template<typename T>
1167  shared_vector<const T> freeze(shared_vector<T>&);
1168 
1169  template<typename T>
1170  shared_vector<T> thaw(shared_vector<const T>&);
1171 }}
1172 @endcode
1173 
1174 So each consumes a shared_vector with a certain value
1175 const-ness, and returns one with the other.
1176 
1177 The following guarantees are provided by both functions:
1178 
1179 # The returned reference points to a value which is equal to the value referenced
1180  by the argument.
1181 # The returned reference points to a value which is only referenced by
1182  shared_vectors with the same value const-ness as the returned reference.
1183 
1184 @note The argument of both freeze() and thaw() is a non-const
1185 reference which will always be cleared.
1186 
1187 @section vfreeze Freezing
1188 
1189 The act of freezing a shared_vector requires that the shared_vector
1190 passed in must be unique() or an exception is thrown.
1191 No copy is made.
1192 
1193 The possibility of an exception can be avoided by calling the make_unique() on a
1194 shared_vector before passing it to freeze().
1195 This will make a copy if necessary.
1196 
1197 @section vthaw Thawing
1198 
1199 The act of thawing a shared_vector may make a copy of the value
1200 referenced by its argument if this reference is not unique().
1201 
1202 */
pointer data() const
Return Base pointer.
Definition: sharedVector.h:616
shared_vector_base & operator=(const shared_vector_base &o)
Copy an existing vector.
Definition: sharedVector.h:172
shared_vector(size_t c, param_type e)
Allocate (with new[]) a new vector of size c and fill with value e.
Definition: sharedVector.h:334
shared_vector_base(shared_vector< const E > &O, _shared_vector_thaw_tag)
Definition: sharedVector.h:155
void swap(shared_vector_base &o)
Swap the contents of this vector with another.
Definition: sharedVector.h:199
void resize(size_t i)
Grow or shrink array.
Definition: sharedVector.h:457
shared_vector(A d, B b, size_t o, size_t c)
Build vector from raw pointer and cleanup function.
Definition: sharedVector.h:367
bool unique() const
Data is not shared?
Definition: sharedVector.h:216
shared_vector< typename ScalarTypeTraits< ID >::type > allocArray(size_t len)
Allocate an untyped array based on ScalarType.
Definition: sharedVector.h:959
void slice(size_t offset, size_t length=(size_t) -1)
Reduce the view of this shared_vector.
Definition: sharedVector.h:244
size_t size() const
Number of elements visible through this vector.
Definition: sharedVector.h:220
reference at(size_t i) const
Member access.
Definition: sharedVector.h:624
void reserve(size_t i)
Set array capacity.
Definition: sharedVector.h:428
shared_vector(const std::tr1::shared_ptr< E1 > &d, size_t o, size_t c)
Build vector from an existing smart pointer.
Definition: sharedVector.h:356
shared_vector(size_t c)
Allocate (with new[]) a new vector of size c.
Definition: sharedVector.h:329
epicsShareFunc shared_vector< void > allocArray(ScalarType id, size_t len)
Allocate an untyped array based on ScalarType.
#define FORCE_INLINE
Definition: templateMeta.h:20
reference operator[](size_t i) const
Member access Undefined if empty()==true.
Definition: sharedVector.h:620
shared_vector(const shared_vector &o)
Copy an existing vector of same type.
Definition: sharedVector.h:371
shared_vector()
Empty vector (not very interesting)
Definition: sharedVector.h:315
epicsShareFunc bool yajl_parse_helper(std::istream &src, yajl_handle handle)
void resize(size_t i, param_type v)
Grow (and fill) or shrink array.
Definition: sharedVector.h:498
size_t m_offset
Offset in the data array of first visible element.
Definition: sharedVector.h:61
void make_unique()
Ensure (by copying) that this shared_vector is the sole owner of the data array.
Definition: sharedVector.h:526
size_t m_total
Total number of elements between m_offset and the end of data.
Definition: sharedVector.h:65
bool empty() const
shorthand for size()==0
Definition: sharedVector.h:222
shared_vector_base(shared_vector_base< _E_non_const > &O, _shared_vector_freeze_tag)
Definition: sharedVector.h:136
size_t m_count
Number of visible elements between m_offset and end of data.
Definition: sharedVector.h:63
void clear()
Clear contents. size() becomes 0.
Definition: sharedVector.h:210
shared_vector(A v, size_t o, size_t c)
Build vector from a raw pointer.
Definition: sharedVector.h:347
shared_vector_base()
Empty vector (not very interesting)
Definition: sharedVector.h:80