PVData C++  8.0.2
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  this->m_sdata.reset(temp, detail::default_array_deleter<E*>());
438  }catch(...){
439  delete[] temp;
440  throw;
441  }
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  this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
485  }catch(...){
486  delete[] temp;
487  throw;
488  }
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(stype==dtype) {
841  // no convert needed
842  return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
843  } else {
844  // alloc and convert
845  shared_vector<to_t> ret(src.size()/ScalarTypeFunc::elementSize(stype));
846  castUnsafeV(ret.size(),
847  dtype,
848  static_cast<void*>(ret.data()),
849  stype,
850  static_cast<const void*>(src.data()));
851  return const_shared_vector_cast<TO>(ret);
852  }
853  }
854  };
855 }
856 
857 /** @brief Allow converting of shared_vector between types
858  *
859  * Conversion utilizes castUnsafe<TO,FROM>().
860  *
861  * Converting to/from void is supported. Convert to void
862  * is an alias for static_shared_vector_cast<void>().
863  * Convert from void utilizes shared_vector<void>::original_type().
864  *
865  * @throws std::runtime_error if cast is not valid.
866  * @throws std::bad_alloc for out of memory condition
867  */
868 template<typename TO, typename FROM>
869 static FORCE_INLINE
870 shared_vector<TO>
871 shared_vector_convert(const shared_vector<FROM>& src)
872 {
873  return detail::shared_vector_converter<TO,FROM>::op(src);
874 }
875 
876 /** @brief transform a shared_vector<T> to shared_vector<const T>
877  *
878  * Transform a reference to mutable data into a reference to read-only data.
879  * Throws an exception unless the reference to mutable data is unique.
880  * On success the reference to mutable data is cleared.
881  */
882 template<typename SRC>
883 static FORCE_INLINE
884 shared_vector<typename meta::decorate_const<typename SRC::value_type>::type>
885 freeze(SRC& src)
886 {
887  typedef typename meta::decorate_const<typename SRC::value_type>::type const_value;
888  return shared_vector<const_value>(src, detail::_shared_vector_freeze_tag());
889 }
890 
891 /** @brief transform a shared_vector<const T> to shared_vector<T>
892  *
893  * Transform a reference to read-only data into a unique reference to mutable data.
894  *
895  * The reference to read-only data is cleared.
896  */
897 template<typename SRC>
898 static FORCE_INLINE
899 shared_vector<typename meta::strip_const<typename SRC::value_type>::type>
900 thaw(SRC& src)
901 {
902  typedef typename meta::strip_const<typename SRC::value_type>::type value;
903  return shared_vector<value>(src, detail::_shared_vector_thaw_tag());
904 }
905 
906 namespace detail {
907  template<typename TO, typename FROM, class Enable = void>
908  struct const_caster {};
909 
910  template<typename TYPE>
911  struct const_caster<TYPE,const TYPE> {
912  static FORCE_INLINE shared_vector<TYPE> op(shared_vector<const TYPE>& src)
913  {
914  return thaw(src);
915  }
916  };
917 
918  template<typename TYPE>
919  struct const_caster<const TYPE,TYPE> {
920  static FORCE_INLINE shared_vector<const TYPE> op(shared_vector<TYPE>& src)
921  {
922  return freeze(src);
923  }
924  };
925 
926  template<typename TYPE>
927  struct const_caster<TYPE,TYPE> {
928  static FORCE_INLINE shared_vector<TYPE> op(shared_vector<TYPE>& src)
929  {
930  shared_vector<TYPE> ret(src);
931  src.clear();
932  return ret;
933  }
934  };
935 }
936 
937 //! Allows casting from const TYPE -> TYPE.
938 template<typename TO, typename FROM>
939 static FORCE_INLINE
940 shared_vector<TO>
941 const_shared_vector_cast(shared_vector<FROM>& src)
942 {
943  return detail::const_caster<TO,FROM>::op(src);
944 }
945 
946 
947 namespace ScalarTypeFunc {
948  //! Allocate an untyped array based on ScalarType
950 
951  //! Allocate an untyped array based on ScalarType
952  template<ScalarType ID>
953  inline
956  {
957  shared_vector<void> raw(allocArray(ID, len));
958  return static_shared_vector_cast<typename ScalarTypeTraits<ID>::type>(raw);
959  }
960 }
961 
962 }} // namespace epics::pvData
963 
964 // Global operators for shared_vector
965 
966 template<typename A, typename B>
967 bool operator==(const epics::pvData::shared_vector<A>& a,
968  const epics::pvData::shared_vector<B>& b)
969 {
970  if(a.size() != b.size())
971  return false;
972  if(a.dataOffset()==b.dataOffset() && a.dataPtr().get()==b.dataPtr().get())
973  return true;
974  return std::equal(a.begin(), a.end(), b.begin());
975 }
976 
977 template<typename A, typename B>
978 bool operator!=(const epics::pvData::shared_vector<A>& a,
979  const epics::pvData::shared_vector<B>& b)
980 {
981  return !(a==b);
982 }
983 
984 template<typename E>
985 std::ostream& operator<<(std::ostream& strm, const epics::pvData::shared_vector<E>& arr)
986 {
987  strm<<'{'<<arr.size()<<"}[";
988  for(size_t i=0; i<arr.size(); i++) {
989  if(i>10) {
990  strm<<"...";
991  break;
992  }
993  strm<<arr[i];
994  if(i+1<arr.size())
995  strm<<", ";
996  }
997  strm<<']';
998  return strm;
999 }
1000 
1001 
1002 #endif // SHAREDVECTOR_H
1003 
1004 /** @page vectordiff Differences between std::vector and shared_vector
1005  *
1006  * @section diffbehave Differences in behavior
1007  *
1008  * shared_vector models const-ness like shared_ptr. A equivalent of
1009  * 'const std::vector<E>' is 'const shared_vector<const E>'. However,
1010  * it is also possible to have 'const shared_vector<E>' analogous to
1011  * 'E* const' and 'shared_vector<const E>' which is analogous to
1012  * 'const E*'.
1013  *
1014  * Copying a shared_vector, by construction or assignment, does
1015  * not copy its contents. Modifications to one such "copy" effect
1016  * all associated shared_vector instances.
1017  *
1018  * std::vector::reserve(N) has no effect if N<=std::vector::capacity().
1019  * However, like resize(), shared_vector<E>::reserve() has the side
1020  * effect of always calling make_unique().
1021  *
1022  * @section notimpl Parts of std::vector interface not implemented
1023  *
1024  * Mutating methods insert(), erase(), shrink_to_fit(),
1025  * emplace(), and emplace_back() are not implemented.
1026  *
1027  * shared_vector does not model an allocator which is bound to the object.
1028  * Therefore the get_allocator() method and the allocator_type typedef are
1029  * not provided.
1030  *
1031  * The assign() method and the related constructor are not implemented
1032  * at this time.
1033  *
1034  * The comparison operators '>', '>=', '<=', and '<' are not implemented
1035  * at this time.
1036  *
1037  * @section newstuff Parts not found in std::vector
1038  *
1039  * shared_vector has additional constructors from raw pointers
1040  * and shared_ptr s.
1041  *
1042  * Implicit casting is not allowed. Instead use
1043  * const_shared_vector_cast()/freeze()/thaw() (@ref vectorconst)
1044  * to casting between 'T' and 'const T'.
1045  * Use static_shared_vector_cast() to cast between
1046  * void and non-void (same const-ness).
1047  *
1048  * To facilitate safe modification the methods unique() and
1049  * make_unique() are provided.
1050  *
1051  * The slice() method selects a sub-set of the shared_vector.
1052  *
1053  * The low level accessors dataPtr(), dataOffset(), dataCount(),
1054  * and dataTotal().
1055  *
1056  */
1057 
1058 /** @page vectormem Memory Management with shared_vector
1059  *
1060  * The @link epics::pvData::shared_vector shared_vector class @endlink
1061  * is a std::vector like class which implements sharing data by reference counting.
1062  *
1063  * Internally memory is tracked with the shared_ptr reference counting smart pointer.
1064  * This allows a custom destructor to be specified. This allows a vector to borrow
1065  * memory allocated by 3rd party libraries which require special cleanup.
1066  *
1067  * In place element modification is allowed. It is left to user code to ensure
1068  * that such modification is safe, either from application specific knowledge, or by
1069  * calling
1070  * @link paramTable::string_data::make_unique make_unique @endlink
1071  * explicitly, or implicitly by calling
1072  * @link paramTable::shared_vector::resize resize @endlink
1073  * prior to making modifications.
1074  *
1075  @code
1076  extern "C" {
1077  // array embedded in C structure
1078  struct composite {
1079  int other, stuff;
1080  char buf[42];
1081  }
1082 
1083  // Unknown relation between array and handle
1084  typedef void* handle_type;
1085  handle_type mylib_alloc(void);
1086  char *mylib_mem(handle_type);
1087  void mylib_free(handle_type);
1088  }
1089 
1090  // Note that mylibcleaner must be copy constructable
1091  struct mylibcleaner {
1092  handle_type handle;
1093  mylibcleaner(handle_type h) :handle(h) {}
1094  void operator()(char*){ mylib_free(handle);}
1095  };
1096 
1097  struct compcleaner {
1098  void operator()(char* c){ free(c-offsetof(composite,buf)); }
1099  };
1100 
1101  void main() {
1102 
1103  unsigned char* buf=calloc(42,1);
1104 
1105  shared_vector<epicsUInt8> a(buf, &free);
1106 
1107  a.clear(); // calls free(ptr)
1108 
1109 
1110  composite *c=malloc(sizeof(*c));
1111  assert(c!=NULL);
1112 
1113  shared_vector<char> d(c->buf, compcleaner());
1114 
1115  d.clear(); // calls free(ptr-offsetof(composite,buf))
1116 
1117 
1118  void *handle=mylib_alloc();
1119  char *hmem=mylib_mem(handle);
1120  assert(hmem!=NULL);
1121 
1122  shared_vector<epicsUInt8> b(hmem, mylibcleaner(handle));
1123 
1124  b.clear(); // calls mylib_free(handleptr)
1125  }
1126  @endcode
1127  */
1128 
1129 /** @page vectorconst Value const-ness and shared_vector
1130 
1131 The type 'shared_vector<T>' can be thought of as 'T*'.
1132 Like the T pointer there are three related constant types:
1133 
1134 @code
1135  shared_vector<int> v_mutable; // 1
1136  const shared_vector<int> v_const_ref; // 2
1137  shared_vector<const int> v_const_data; // 3
1138  const shared_vector<const int> v_const_ref_data; // 4
1139 @endcode
1140 
1141 The distinction between these types is what "part" of the type is constant,
1142 the "reference" (pointer) or the "value" (location being pointed to).
1143 
1144 Type #2 is constant reference to a mutable value.
1145 Type #3 is a mutable reference to a constant value.
1146 Type #4 is a constant reference to a constant value.
1147 
1148 Casting between const and non-const values does @b not follow the normal
1149 C++ casting rules (no implicit cast).
1150 
1151 For casting between shared_vector<T> and shared_vector<const T>
1152 explicit casting operations are required. These operations are
1153 @b freeze() (non-const to const) and @b thaw() (const to non-const).
1154 
1155 A 'shared_vector<const T>' is "frozen" as its value can not be modified.
1156 However it can still be sliced because the reference is not const.
1157 
1158 These functions are defined like:
1159 
1160 @code
1161 namespace epics{namespace pvData{
1162  template<typename T>
1163  shared_vector<const T> freeze(shared_vector<T>&);
1164 
1165  template<typename T>
1166  shared_vector<T> thaw(shared_vector<const T>&);
1167 }}
1168 @endcode
1169 
1170 So each consumes a shared_vector with a certain value
1171 const-ness, and returns one with the other.
1172 
1173 The following guarantees are provided by both functions:
1174 
1175 # The returned reference points to a value which is equal to the value referenced
1176  by the argument.
1177 # The returned reference points to a value which is only referenced by
1178  shared_vectors with the same value const-ness as the returned reference.
1179 
1180 @note The argument of both freeze() and thaw() is a non-const
1181 reference which will always be cleared.
1182 
1183 @section vfreeze Freezing
1184 
1185 The act of freezing a shared_vector requires that the shared_vector
1186 passed in must be unique() or an exception is thrown.
1187 No copy is made.
1188 
1189 The possibility of an exception can be avoided by calling the make_unique() on a
1190 shared_vector before passing it to freeze().
1191 This will make a copy if necessary.
1192 
1193 @section vthaw Thawing
1194 
1195 The act of thawing a shared_vector may make a copy of the value
1196 referenced by its argument if this reference is not unique().
1197 
1198 */
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:955
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