Files
pvData/pvDataApp/misc/sharedVector.h
2013-11-27 01:11:12 +01:00

1014 lines
32 KiB
C++

/* sharedVector.h */
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#ifndef SHAREDVECTOR_H
#define SHAREDVECTOR_H
#ifdef _WIN32
#define NOMINMAX
#endif
#include <ostream>
#include <algorithm>
#include <stdexcept>
#include <iterator>
#include <cassert>
#include "pv/sharedPtr.h"
#include "pv/pvIntrospect.h"
#include "pv/typeCast.h"
#include "pv/templateMeta.h"
namespace epics { namespace pvData {
template<typename E, class Enable = void> class shared_vector;
template<typename TO, typename FROM>
static FORCE_INLINE
shared_vector<TO>
const_shared_vector_cast(shared_vector<FROM>& src);
namespace detail {
template<typename E>
struct default_array_deleter {void operator()(E a){delete[] a;}};
// How values should be passed as arguments to shared_vector methods
// really should use boost::call_traits
template<typename T> struct call_with { typedef T type; };
template<typename T> struct call_with<std::tr1::shared_ptr<T> >
{ typedef const std::tr1::shared_ptr<T>& type; };
template<> struct call_with<std::string> { typedef const std::string& type; };
struct _shared_vector_freeze_tag {};
struct _shared_vector_thaw_tag {};
struct _shared_vector_cast_tag {};
/* All the parts of shared_vector which
* don't need special handling for E=void
*/
template<typename E>
class shared_vector_base
{
// allow specialization for all E to be friends
template<typename E1> friend class shared_vector_base;
protected:
std::tr1::shared_ptr<E> m_data;
//! Offset in the data array of first visible element
size_t m_offset;
//! Number of visible elements between m_offset and end of data
size_t m_count;
//! Total number of elements between m_offset and the end of data
size_t m_total;
/* invariants
* m_count <= m_total (enforced)
* m_offset + m_total <= (size_t)-1 (checked w/ assert())
*/
public:
//! @brief Empty vector (not very interesting)
shared_vector_base()
:m_data(), m_offset(0), m_count(0), m_total(0)
{}
protected:
// helper for constructors
// Ensure that offset and size are zero when we are constructed with NULL
void _null_input()
{
if(!m_data.get()) {
m_offset = m_total = m_count = 0;
} else {
// ensure we won't have integer overflows later
assert( m_offset <= ((size_t)-1) - m_total);
}
}
public:
#ifdef _WIN32
template<typename A>
shared_vector_base(A* v, size_t o, size_t c)
:m_data(v, detail::default_array_deleter<A*>())
,m_offset(o), m_count(c), m_total(c)
{_null_input();}
#else
template<typename A>
shared_vector_base(A v, size_t o, size_t c)
:m_data(v, detail::default_array_deleter<A>())
,m_offset(o), m_count(c), m_total(c)
{_null_input();}
#endif
shared_vector_base(const std::tr1::shared_ptr<E>& d, size_t o, size_t c)
:m_data(d), m_offset(o), m_count(c), m_total(c)
{_null_input();}
template<typename A, typename B>
shared_vector_base(A d, B b, size_t o, size_t c)
:m_data(d,b), m_offset(o), m_count(c), m_total(c)
{_null_input();}
shared_vector_base(const shared_vector_base& O)
:m_data(O.m_data), m_offset(O.m_offset)
,m_count(O.m_count), m_total(O.m_total)
{}
protected:
typedef typename meta::strip_const<E>::type _E_non_const;
public:
//! Constructor used to implement freeze().
//! Should not be called directly.
shared_vector_base(shared_vector_base<_E_non_const>& O,
_shared_vector_freeze_tag)
:m_data()
,m_offset(O.m_offset)
,m_count(O.m_count)
,m_total(O.m_total)
{
if(!O.unique())
throw std::runtime_error("Can't freeze non-unique vector");
m_data = O.m_data;
O.clear();
}
//! Constructor used to implement thaw().
//! Should not be called directly.
shared_vector_base(shared_vector<const E>& O,
_shared_vector_thaw_tag)
:m_data()
,m_offset(O.m_offset)
,m_count(O.m_count)
,m_total(O.m_total)
{
O.make_unique();
m_data = std::tr1::const_pointer_cast<E>(O.m_data);
O.clear();
}
//! @brief Copy an existing vector
shared_vector_base& operator=(const shared_vector_base& o)
{
if(&o!=this) {
m_data=o.m_data;
m_offset=o.m_offset;
m_count=o.m_count;
m_total=o.m_total;
}
return *this;
}
//! @brief Swap the contents of this vector with another
void swap(shared_vector_base& o) {
if(&o!=this) {
m_data.swap(o.m_data);
std::swap(m_count, o.m_count);
std::swap(m_offset, o.m_offset);
std::swap(m_total, o.m_total);
}
}
//! @brief Clear contents.
//! size() becomes 0
void clear() {
m_data.reset();
m_offset = m_total = m_count = 0;
}
//! @brief Data is not shared?
bool unique() const {return !m_data || m_data.unique();}
//! @brief Number of elements visible through this vector
size_t size() const{return m_count;}
bool empty() const{return !m_count;}
/** @brief Reduce the view of this shared_vector.
*
* Reduce the portion of the underlying buffer which
* is accessible through this shared_vector.
*
* When the requested new offset and length are not possible
* then the following holds.
*
* When offset is >= size() then after slice() size()==0.
* When length >= size()-offset then after slice()
* size() = old_size-offset.
*
@param offset The request new offset relative to the
* current offset.
@param length The requested new length.
*
@note offset and length are in units of sizeof(E).
* or bytes (1) when E=void.
*/
void slice(size_t offset, size_t length=(size_t)-1)
{
if(offset>m_count)
offset = m_count; // will slice down to zero length
const size_t max_count = m_count - offset;
m_offset += offset;
m_total -= offset;
m_count = std::min(length, max_count);
}
// Access to members.
const std::tr1::shared_ptr<E>& dataPtr() const { return m_data; }
size_t dataOffset() const { return m_offset; }
size_t dataCount() const { return m_count; }
size_t dataTotal() const { return m_total; }
};
}
/** @brief A holder for a contigious piece of memory.
*
* Data is shared, but offset and length are not.
* This allows one vector to have access to only a
* subset of a piece of memory.
*
* The ways in which shared_vector is intended to differ from
* std::vector are outlined in @ref vectordiff .
*
* Also see @ref vectormem and @ref vectorconst
*
* @warning Due to the implementation of std::tr1::shared_ptr, use of
* shared_vector should not be combined with use of weak_ptr.
* shared_ptr::unique() and shared_ptr::use_count() do @b not
* include weak_ptr instances. This breaks the assumption made
* by make_unique() that unique()==true implies exclusive
* ownership.
*/
template<typename E, class Enable>
class shared_vector : public detail::shared_vector_base<E>
{
typedef detail::shared_vector_base<E> base_t;
typedef typename detail::call_with<E>::type param_type;
typedef typename meta::strip_const<E>::type _E_non_const;
public:
typedef E value_type;
typedef E& reference;
typedef typename meta::decorate_const<E>::type& const_reference;
typedef E* pointer;
typedef typename meta::decorate_const<E>::type* const_pointer;
typedef E* iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef typename meta::decorate_const<E>::type* const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef E element_type;
typedef std::tr1::shared_ptr<E> shared_pointer_type;
// allow specialization for all E to be friends
template<typename E1, class Enable1> friend class shared_vector;
//! @brief Empty vector (not very interesting)
shared_vector() :base_t() {}
//! @brief Allocate (with new[]) a new vector of size c
explicit shared_vector(size_t c)
:base_t(new _E_non_const[c], 0, c)
{}
//! @brief Allocate (with new[]) a new vector of size c and fill with value e
shared_vector(size_t c, param_type e)
:base_t(new _E_non_const[c], 0, c)
{
std::fill_n((_E_non_const*)this->m_data.get(), this->m_count, e);
}
/** @brief Build vector from a raw pointer
*
@param v A raw pointer allocated with new[].
@param o The offset in v or the first element visible to the vector
@param c The number of elements pointed to by v+o
*/
template<typename A>
shared_vector(A v, size_t o, size_t c) :base_t(v,o,c) {}
/** @brief Build vector from an existing smart pointer
*
@param d An existing smart pointer
@param o The offset in v or the first element visible to the vector
@param c The number of elements pointed to by v+o
*/
template<typename E1>
shared_vector(const std::tr1::shared_ptr<E1>& d, size_t o, size_t c)
:base_t(d,o,c) {}
/** @brief Build vector from raw pointer and cleanup function
*
@param d An existing raw pointer
@param b An function/functor used to free d. Invoked as b(d).
@param o The offset in v or the first element visible to the vector
@param c The number of elements pointed to by v+o
*/
template<typename A, typename B>
shared_vector(A d, B b, size_t o, size_t c)
:base_t(d,b,o,c) {}
//! @brief Copy an existing vector of same type
shared_vector(const shared_vector& o) :base_t(o) {}
//! @internal
//! Internal for static_shared_vector_cast
template<typename FROM>
shared_vector(const shared_vector<FROM> &src,
typename meta::is_void<FROM, detail::_shared_vector_cast_tag>::type)
:base_t(std::tr1::static_pointer_cast<E>(src.dataPtr()),
src.dataOffset()/sizeof(E),
src.dataCount()/sizeof(E))
{}
shared_vector(shared_vector<typename base_t::_E_non_const>& O,
detail::_shared_vector_freeze_tag t)
:base_t(O,t)
{}
shared_vector(shared_vector<const E>& O,
detail::_shared_vector_thaw_tag t)
:base_t(O,t)
{}
size_t max_size() const{return ((size_t)-1)/sizeof(E);}
size_t capacity() const { return this->m_total; }
/** @brief Set array capacity
*
* A side effect is that array data will be uniquely owned by this instance
* as if make_unique() was called. This holds even if the capacity
* does not increase.
*
* For notes on copying see docs for make_unique().
*/
void reserve(size_t i) {
if(this->unique() && i<=this->m_total)
return;
_E_non_const* temp=new _E_non_const[i];
try{
std::copy(begin(), end(), temp);
this->m_data.reset(temp, detail::default_array_deleter<E*>());
}catch(...){
delete[] temp;
throw;
}
this->m_offset = 0;
this->m_total = i;
// m_count is unchanged
}
/** @brief Grow or shrink array
*
* A side effect is that array data will be uniquely owned by this instance
* as if make_unique() were called. This holds even if the size does not change.
*
* For notes on copying see docs for make_unique().
*/
void resize(size_t i) {
if(i==this->m_count) {
make_unique();
return;
}
if(this->m_data && this->m_data.unique()) {
// we have data and exclusive ownership of it
if(i<=this->m_total) {
// We have room to grow (or shrink)!
this->m_count = i;
return;
}
}
// must re-allocate :(
size_t new_total = std::max(this->m_total, i);
_E_non_const* temp=new _E_non_const[new_total];
try{
// Copy as much as possible from old,
// remaining elements are uninitialized.
std::copy(begin(),
begin()+std::min(i,this->size()),
temp);
this->m_data.reset(temp, detail::default_array_deleter<pointer>());
}catch(...){
delete[] temp;
throw;
}
this->m_offset= 0;
this->m_count = i;
this->m_total = new_total;
}
/** @brief Grow (and fill) or shrink array.
*
* see @ref resize(size_t)
*/
void resize(size_t i, param_type v) {
size_t oldsize=this->size();
resize(i);
if(this->size()>oldsize) {
std::fill(begin()+oldsize, end(), v);
}
}
/** @brief Ensure (by copying) that this shared_vector is the sole
* owner of the data array.
*
* If a copy is needed, memory is allocated with new[]. If this is
* not desireable then do something like the following.
@code
shared_vector<E> original(...);
if(!original.unique()){
shared_vector<E> temp(myallocator(original.size()),
0, original.size());
std::copy(original.begin(), original.end(), temp.begin());
original.swap(temp);
}
assert(original.unique());
@endcode
*/
void make_unique() {
if(this->unique())
return;
_E_non_const *d = new _E_non_const[this->m_total];
try {
std::copy(this->m_data.get()+this->m_offset,
this->m_data.get()+this->m_offset+this->m_count,
d);
}catch(...){
delete[] d;
throw;
}
this->m_data.reset(d, detail::default_array_deleter<E*>());
this->m_offset=0;
}
// STL iterators
iterator begin() const{return this->m_data.get()+this->m_offset;}
const_iterator cbegin() const{return begin();}
iterator end() const{return this->m_data.get()+this->m_offset+this->m_count;}
const_iterator cend() const{return end();}
reverse_iterator rbegin() const{return reverse_iterator(end());}
const_reverse_iterator crbegin() const{return rbegin();}
reverse_iterator rend() const{return reverse_iterator(begin());}
const_reverse_iterator crend() const{return rend();}
reference front() const{return (*this)[0];}
reference back() const{return (*this)[this->m_count-1];}
// Modifications
private:
void _push_resize() {
if(this->m_count==this->m_total || !this->unique()) {
size_t next;
if(this->m_total<1024) {
// round m_total+1 up to the next power of 2
next = this->m_total;
next |= next >> 1;
next |= next >> 2;
next |= next >> 4;
next |= next >> 8;
next++;
} else {
// pad m_total up to the next multiple of 1024
next = this->m_total+1024;
next &= ~0x3ff;
}
assert(next > this->m_total);
reserve(next);
}
resize(this->size()+1);
}
public:
void push_back(param_type v)
{
_push_resize();
back() = v;
}
void pop_back()
{
this->slice(0, this->size()-1);
}
// data access
pointer data() const{return this->m_data.get()+this->m_offset;}
reference operator[](size_t i) const {return this->m_data.get()[this->m_offset+i];}
reference at(size_t i) const
{
if(i>this->m_count)
throw std::out_of_range("Index out of bounds");
return (*this)[i];
}
};
//! Specialization for storing untyped pointers
//! Does not allow access or iteration of contents
//! other than as void* or const void*
template<typename E>
class shared_vector<E, typename meta::is_void<E>::type >
: public detail::shared_vector_base<E>
{
typedef detail::shared_vector_base<E> base_t;
ScalarType m_vtype;
public:
typedef E* pointer;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef std::tr1::shared_ptr<E> shared_pointer_type;
shared_vector() :base_t(), m_vtype((ScalarType)-1) {}
shared_vector(pointer v, size_t o, size_t c)
:base_t(v,o,c), m_vtype((ScalarType)-1) {}
template<typename B>
shared_vector(pointer d, B b, size_t o, size_t c)
:base_t(d,b,o,c), m_vtype((ScalarType)-1) {}
template<typename E1>
shared_vector(const std::tr1::shared_ptr<E1>& d, size_t o, size_t c)
:base_t(d,o,c), m_vtype((ScalarType)-1) {}
shared_vector(const shared_vector& o)
:base_t(o), m_vtype(o.m_vtype) {}
//! @internal
//! Internal for static_shared_vector_cast
template<typename FROM>
shared_vector(const shared_vector<FROM> &src,
typename meta::is_not_void<FROM, detail::_shared_vector_cast_tag>::type)
:base_t(std::tr1::static_pointer_cast<E>(src.dataPtr()),
src.dataOffset()*sizeof(FROM),
src.dataCount()*sizeof(FROM))
,m_vtype((ScalarType)ScalarTypeID<FROM>::value)
{}
shared_vector(shared_vector<void>& O,
detail::_shared_vector_freeze_tag t)
:base_t(O,t)
{}
shared_vector(shared_vector<const void>& O,
detail::_shared_vector_thaw_tag t)
:base_t(O,t)
{}
shared_vector& operator=(const shared_vector& o)
{
if(&o!=this) {
this->base_t::operator=(o);
m_vtype = o.m_vtype;
}
return *this;
}
size_t max_size() const{return (size_t)-1;}
pointer data() const{
return (pointer)(((char*)this->m_data.get())+this->m_offset);
}
shared_vector& set_original_type(ScalarType t) { m_vtype=t; return *this; }
ScalarType original_type() const {return m_vtype;}
};
namespace detail {
// Default to type conversion using castUnsafe (C++ type casting) on each element
template<typename TO, typename FROM, class Enable = void>
struct shared_vector_converter {
static inline shared_vector<TO> op(const shared_vector<FROM>& src)
{
shared_vector<TO> ret(src.size());
std::transform(src.begin(), src.end(), ret.begin(), castUnsafe<TO,FROM>);
return ret;
}
};
// copy reference when types are the same (excluding const qualifiers)
template<typename TO, typename FROM>
struct shared_vector_converter<TO,FROM, typename meta::same_root<TO,FROM>::type > {
static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
return src;
}
};
// "convert" to 'void' or 'const void from non-void
// is an alias for shared_vector_cast<void>()
template<typename TO, typename FROM>
struct shared_vector_converter<TO,FROM,
typename meta::_and<meta::is_void<TO>, meta::is_not_void<FROM> >::type
>
{
static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
}
};
// convert from void uses original type or throws an exception.
template<typename TO, typename FROM>
struct shared_vector_converter<TO,FROM,
typename meta::_and<meta::is_not_void<TO>, meta::is_void<FROM> >::type
>
{
static shared_vector<TO> op(const shared_vector<FROM>& src) {
typedef typename meta::strip_const<TO>::type to_t;
ScalarType stype = src.original_type(),
dtype = (ScalarType)ScalarTypeID<TO>::value;
if(stype==dtype) {
// no convert needed
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
} else {
// alloc and convert
shared_vector<to_t> ret(src.size()/ScalarTypeFunc::elementSize(stype));
castUnsafeV(ret.size(),
dtype,
static_cast<void*>(ret.data()),
stype,
static_cast<const void*>(src.data()));
return const_shared_vector_cast<TO>(ret);
}
}
};
}
/** @brief Allow casting of shared_vector between types
*
* Currently only to/from void is implemented.
*
@warning Casting from void is undefined unless the offset and length
* are integer multiples of the size of the destination type.
*/
template<typename TO, typename FROM>
static FORCE_INLINE
shared_vector<TO>
static_shared_vector_cast(const shared_vector<FROM>& src,
typename meta::same_const<TO,FROM,int>::type = 0)
{
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
}
/** @brief Allow converting of shared_vector between types
*
* Conversion utilizes castUnsafe<TO,FROM>().
*
* Converting to/from void is supported. Convert to void
* is an alias for static_shared_vector_cast<void>().
* Convert from void utilizes shared_vector<void>::original_type()
* and throws std::runtime_error if this is not valid.
*/
template<typename TO, typename FROM>
static FORCE_INLINE
shared_vector<TO>
shared_vector_convert(const shared_vector<FROM>& src)
{
return detail::shared_vector_converter<TO,FROM>::op(src);
}
/** @brief transform a shared_vector<T> to shared_vector<const T>
*
* Transform a reference to mutable data into a reference to read-only data.
* Throws an exception unless the reference to mutable data is unique.
* On success the reference to mutable data is cleared.
*/
template<typename SRC>
static FORCE_INLINE
shared_vector<typename meta::decorate_const<typename SRC::value_type>::type>
freeze(SRC& src)
{
typedef typename meta::decorate_const<typename SRC::value_type>::type const_value;
return shared_vector<const_value>(src, detail::_shared_vector_freeze_tag());
}
/** @brief transform a shared_vector<const T> to shared_vector<T>
*
* Transform a reference to read-only data into a unique reference to mutable data.
*
* The reference to read-only data is cleared.
*/
template<typename SRC>
static FORCE_INLINE
shared_vector<typename meta::strip_const<typename SRC::value_type>::type>
thaw(SRC& src)
{
typedef typename meta::strip_const<typename SRC::value_type>::type value;
return shared_vector<value>(src, detail::_shared_vector_thaw_tag());
}
namespace detail {
template<typename TO, typename FROM, class Enable = void>
struct const_caster {};
template<typename TYPE>
struct const_caster<TYPE,const TYPE> {
static FORCE_INLINE shared_vector<TYPE> op(shared_vector<const TYPE>& src)
{
return thaw(src);
}
};
template<typename TYPE>
struct const_caster<const TYPE,TYPE> {
static FORCE_INLINE shared_vector<const TYPE> op(shared_vector<TYPE>& src)
{
return freeze(src);
}
};
template<typename TYPE>
struct const_caster<TYPE,TYPE> {
static FORCE_INLINE shared_vector<TYPE> op(shared_vector<TYPE>& src)
{
shared_vector<TYPE> ret(src);
src.clear();
return ret;
}
};
}
//! Allows casting from const TYPE -> TYPE.
template<typename TO, typename FROM>
static FORCE_INLINE
shared_vector<TO>
const_shared_vector_cast(shared_vector<FROM>& src)
{
return detail::const_caster<TO,FROM>::op(src);
}
namespace ScalarTypeFunc {
//! Allocate an untyped array based on ScalarType
shared_vector<void> allocArray(ScalarType id, size_t len);
//! Allocate an untyped array based on ScalarType
template<ScalarType ID>
inline
shared_vector<typename ScalarTypeTraits<ID>::type>
allocArray(size_t len)
{
shared_vector<void> raw(allocArray(ID, len));
return static_shared_vector_cast<typename ScalarTypeTraits<ID>::type>(raw);
}
}
}} // namespace epics::pvData
// Global operators for shared_vector
template<typename A, typename B>
bool operator==(const epics::pvData::shared_vector<A>& a,
const epics::pvData::shared_vector<B>& b)
{
if(a.size() != b.size())
return false;
if(a.dataOffset()==b.dataOffset() && a.dataPtr().get()==b.dataPtr().get())
return true;
return std::equal(a.begin(), a.end(), b.begin());
}
template<typename A, typename B>
bool operator!=(const epics::pvData::shared_vector<A>& a,
const epics::pvData::shared_vector<B>& b)
{
return !(a==b);
}
template<typename E>
std::ostream& operator<<(std::ostream& strm, const epics::pvData::shared_vector<E>& arr)
{
strm<<'{'<<arr.size()<<"}[";
for(size_t i=0; i<arr.size(); i++) {
if(i>10) {
strm<<"...";
break;
}
strm<<arr[i];
if(i+1<arr.size())
strm<<", ";
}
strm<<']';
return strm;
}
#endif // SHAREDVECTOR_H
/** @page vectordiff Differences between std::vector and shared_vector
*
* @section diffbehave Differences in behavior
*
* shared_vector models const-ness like shared_ptr. A equivalent of
* 'const std::vector<E>' is 'const shared_vector<const E>'. However,
* it is also possible to have 'const shared_vector<E>' analogous to
* 'E* const' and 'shared_vector<const E>' which is analogous to
* 'const E*'.
*
* Copying a shared_vector, by construction or assignment, does
* not copy its contents. Modifications to one such "copy" effect
* all associated shared_vector instances.
*
* std::vector::reserve(N) has no effect if N<=std::vector::capacity().
* However, like resize(), shared_vector<E>::reserve() has the side
* effect of always calling make_unique().
*
* @section notimpl Parts of std::vector interface not implemented
*
* Mutating methods insert(), erase(), shrink_to_fit(),
* emplace(), and emplace_back() are not implemented.
*
* shared_vector does not model an allocator which is bound to the object.
* Therefore the get_allocator() method and the allocator_type typedef are
* not provided.
*
* The assign() method and the related constructor are not implemented
* at this time.
*
* The comparison operators '>', '>=', '<=', and '<' are not implemented
* at this time.
*
* @section newstuff Parts not found in std::vector
*
* shared_vector has additional constructors from raw pointers
* and shared_ptr s.
*
* The copy constructor and assignment operator allow implicit
* casting from type 'shared_vector<T>' to 'shared_vector<const T>'.
*
* To faciliate safe modification the methods unique() and
* make_unique() are provided.
*
* The slice() method selects a sub-set of the shared_vector.
*
* The low level accessors dataPtr(), dataOffset(), dataCount(),
* and dataTotal().
*
*/
/** @page vectormem Memory Management with shared_vector
*
* The @link epics::pvData::shared_vector shared_vector class @endlink
* is a std::vector like class which implements sharing data by reference counting.
*
* Internally memory is tracked with the shared_ptr reference counting smart pointer.
* This allows a custom destructor to be specified. This allows a vector to borrow
* memory allocated by 3rd party libraries which require special cleanup.
*
* In place element modification is allowed. It is left to user code to ensure
* that such modification is safe, either from application specific knowledge, or by
* calling
* @link paramTable::string_data::make_unique make_unique @endlink
* explicitly, or implicitly by calling
* @link paramTable::shared_vector::resize resize @endlink
* prior to making modifications.
*
@code
extern "C" {
// array embedded in C structure
struct composite {
int other, stuff;
char buf[42];
}
// Unknown relation between array and handle
typedef void* handle_type;
handle_type mylib_alloc(void);
char *mylib_mem(handle_type);
void mylib_free(handle_type);
}
// Note that mylibcleaner must be copy constructable
struct mylibcleaner {
handle_type handle;
mylibcleaner(handle_type h) :handle(h) {}
void operator()(char*){ mylib_free(handle);}
};
struct compcleaner {
void operator()(char* c){ free(c-offsetof(composite,buf)); }
};
void main() {
unsigned char* buf=calloc(42,1);
shared_vector<epicsUInt8> a(buf, &free);
a.clear(); // calls free(ptr)
composite *c=malloc(sizeof(*c));
assert(c!=NULL);
shared_vector<char> d(c->buf, compcleaner());
d.clear(); // calls free(ptr-offsetof(composite,buf))
void *handle=mylib_alloc();
char *hmem=mylib_mem(handle);
assert(hmem!=NULL);
shared_vector<epicsUInt8> b(hmem, mylibcleaner(handle));
b.clear(); // calls mylib_free(handleptr)
}
@endcode
*/
/** @page vectorconst Value const-ness and shared_vector
The type 'shared_vector<T>' can be thought of as 'T*'.
Like the T pointer there are three related constant types:
@code
shared_vector<int> v_mutable; // 1
const shared_vector<int> v_const_ref; // 2
shared_vector<const int> v_const_data; // 3
const shared_vector<const int> v_const_ref_data; // 4
@endcode
The distinction between these types is what "part" of the type is constant,
the "reference" (pointer) or the "value" (location being pointed to).
Type #2 is constant reference to a mutable value.
Type #3 is a mutable reference to a constant value.
Type #4 is a constant reference to a constant value.
Casting between const and non-const references of the same value type
is governed by the normal C++ casting rules.
Casting between const and non-const values does @b not follow the normal
C++ casting rules.
For casting between shared_vector<T> and shared_vector<const T>
explicit casting operations are required. These operations are
@b freeze() (non-const to const) and @b thaw() (const to non-const).
A shared_vector<const T> is "frozen" as its value can not be modified.
These functions are defined like:
@code
namespace epics{namespace pvData{
template<typename T>
shared_vector<const T> freeze(shared_vector<T>&);
template<typename T>
shared_vector<T> thaw(shared_vector<const T>&);
}}
@endcode
So each consumes a shared_vector with a certain value
const-ness, and returns one with the other.
The following guarantees are provided by both functions:
# The returned reference points to a value which is equal to the value referenced
by the argument.
# The returned reference points to a value which is only referenced by
shared_vectors with the same value const-ness as the returned reference.
Please note that the argument of both freeze and thaw is a non-const
reference which will always be cleared.
@section vfreeze Freezing
The act of freezing a shared_vector requires that the shared_vector
passed in must be unique() or an exception is thrown. This is
done to reduce the possibility of accidental copying.
This possibility can be avoided by calling the make_unique() on a
shared_vector before passing it to freeze().
@section vthaw Thawing
The act of thawing a shared_vector may make a copy of the value
referenced by its argument if this reference is not unique().
*/