#ifndef VALUE_ARRAY_H #define VALUE_ARRAY_H #define DBVALUEARRAY(x) x #include #include #include #include #include #include #include #include #ifdef USE_BOOST_SERIALIZE # include # include #endif //TODO: use awesome type traits (and/or policy typelist argument) to provide these only when possible? #define VALUE_ARRAY_ADD 1 #define VALUE_ARRAY_MUL 1 #define VALUE_ARRAY_BITWISE 0 #define VALUE_ARRAY_OSTREAM 1 #if VALUE_ARRAY_OSTREAM # include #endif // valarray like in that size is fixed (so saves space compared to vector), but same interface as vector (less resize/push_back/insert, of course) template > class ValueArray : A { // private inheritance so stateless allocator adds no size. typedef ValueArray Self; public: #if VALUE_ARRAY_OSTREAM friend inline std::ostream & operator << (std::ostream &o,Self const& s) { o<<'['; for (unsigned i=0,e=s.size();i1?sizeof(T)/sizeof(T*):1; //space optimization: SV_MAX T will fit inside what would otherwise be a pointer to heap data. todo in the far future if bored. typedef T value_type; typedef T& reference; typedef T const& const_reference; typedef T* iterator; typedef T const* const_iterator; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef T* pointer; size_type size() const { return sz; } bool empty() const { return !sz; } iterator begin() { return array; } iterator end() { return array + sz; } const_iterator begin() const { return array; } const_iterator end() const { return array + sz; } reference operator[](size_type pos) { return array[pos]; } const_reference operator[](size_type pos) const { return array[pos]; } reference at(size_type pos) { return array[pos]; } const_reference at(size_type pos) const { return array[pos]; } reference front() { return array[0]; } reference back() { return array[sz-1]; } const_reference front() const { return array[0]; } const_reference back() const { return array[sz-1]; } ValueArray() : sz(0), array(NULL) {} friend inline std::size_t hash_value(Self const& x) { return boost::hash_range(x.begin(),x.end()); } protected: void destroy() { if (!array) return; // it's cool that this destroys in reverse order of construction, but why bother? for (pointer i=array+sz;i>array;) A::destroy(--i); } void dealloc() { if (sz && array != NULL) A::deallocate(array,sz); sz=0; } void alloc(size_type s) { array=s?A::allocate(s):0; sz=s; } void realloc(size_type s) { if (sz!=s) { dealloc(); alloc(s); } } void reinit_noconstruct(size_type s) { destroy(); realloc(s); } template inline void init_map(C & c,F const& f) { alloc(c.size()); copy_construct_map(c.begin(),c.end(),array,f); } template inline void init_map(C const& c,F const& f) { alloc(c.size()); copy_construct_map(c.begin(),c.end(),array,f); } // warning: std::distance is likely slow on maps (anything other than random access containers. so container version using size will be better template inline void init_range_map(I itr, I end,F const& f) { alloc(std::distance(itr,end)); copy_construct_map(itr,end,array,f); } template inline void init_range(I itr, I end) { alloc(std::distance(itr,end)); copy_construct(itr,end,array); } inline void fill(const_reference t) { for (pointer i=array,e=array+sz;i!=e;++i) new(i) T(t); } inline void fill() { for (pointer i=array,e=array+sz;i!=e;++i) new(i) T(); } inline void init(size_type s) { alloc(s); fill(); } inline void init(size_type s, const_reference t) { alloc(s); fill(t); } public: ValueArray(size_type s, const_reference t) { init(s,t); } explicit ValueArray(size_type s) { init(s); } void reinit(size_type s, const_reference t) { reinit_noconstruct(s); fill(t); } void reinit(size_type s) { reinit_noconstruct(s); fill(); } //copy any existing data like std::vector. not A::construct exception safe. try blah blah? swap? void resize(size_type s, const_reference t = T()) { if (s) { pointer na=A::allocate(s); size_type nc=s void reinit_range(I itr, I end) { reinit_noconstruct(std::distance(itr,end)); copy_construct(itr,end,array); } template void reinit_map(I itr,I end,F const& f) { reinit_noconstruct(std::distance(itr,end)); copy_construct_map(itr,end,array,f); } // warning: std::distance is likely slow on maps,lists (anything other than random access containers. so container version below using size() will be better template void reinit_map(C const& c,F const& f) { reinit_noconstruct(c.size()); copy_construct_map(c.begin(),c.end(),array,f); } template void reinit_map(C & c,F const& f) { reinit_noconstruct(c.size()); copy_construct_map(c.begin(),c.end(),array,f); } template ValueArray(I itr, I end) { init_range(itr,end); } template ValueArray(I itr, I end,F const& map) { init_range_map(itr,end,map); } ~ValueArray() { clear(); } #undef VALUE_ARRAY_OPEQ #define VALUE_ARRAY_OPEQ(op) template Self & operator op (ValueArray const& o) { assert(sz==o.sz); for (int i=0,e=sz;i<=e;++i) array[i] op o.array[i]; return *this; } #if VALUE_ARRAY_ADD VALUE_ARRAY_OPEQ(+=) VALUE_ARRAY_OPEQ(-=) #endif #if VALUE_ARRAY_MUL VALUE_ARRAY_OPEQ(*=) VALUE_ARRAY_OPEQ(/=) #endif #if VALUE_ARRAY_BITWISE VALUE_ARRAY_OPEQ(|=) VALUE_ARRAY_OPEQ(*=) #endif #undef VALUE_ARRAY_OPEQ #undef VALUE_ARRAY_BINOP #define VALUE_ARRAY_BINOP(op,opeq) template friend inline Self operator op (Self x,ValueArray const& y) { x opeq y; return x; } #if VALUE_ARRAY_ADD VALUE_ARRAY_BINOP(+,+=) VALUE_ARRAY_BINOP(-,-=) #endif #if VALUE_ARRAY_MUL VALUE_ARRAY_BINOP(*,*=) VALUE_ARRAY_BINOP(/,/=) #endif #if VALUE_ARRAY_BITWISE VALUE_ARRAY_BINOP(|,|=) VALUE_ARRAY_BINOP(*,*=) #endif #undef VALUE_ARRAY_BINOP void clear() { destroy(); dealloc(); } void swap(ValueArray& other) { std::swap(sz,other.sz); std::swap(array,other.array); } ValueArray(ValueArray const& other) : A(other) , sz(other.sz) , array(sz?A::allocate(sz):0) { copy_construct(other.begin(),other.end(),array); } ValueArray& operator=(ValueArray const& other) { ValueArray(other).swap(*this); return *this; } template ValueArray( Range const& v , typename boost::disable_if< boost::is_integral >::type* = 0) : sz(boost::size(v)) , array(sz?A::allocate(sz):0) { copy_construct(boost::begin(v),boost::end(v),array); } template typename boost::disable_if< boost::is_integral , ValueArray>::type& operator=(Range const& other) { ValueArray(other).swap(*this); return *this; } private: template void copy_construct(I1 itr, I1 end, T *into) { for (; itr != end; ++itr, ++into) A::construct(into,*itr); } //TODO: valgrind doesn't think this works. template void copy_construct_map(I1 itr, I1 end, T *into,F const& f) { while ( itr != end) { DBVALUEARRAY(assert(into void save(Archive& ar, unsigned int /*version*/) const { ar << sz; for (size_type i = 0; i != sz; ++i) ar << at(i); } template void load(Archive& ar, unsigned int /*version*/) { size_type s; ar >> s; ValueArray v(s); for (size_type i = 0; i != s; ++i) ar >> v[i]; this->swap(v); } #ifdef USE_BOOST_SERIALIZE BOOST_SERIALIZATION_SPLIT_MEMBER() #endif private: size_type sz; pointer array; }; template bool operator==(ValueArray const& v1, ValueArray const& v2) { return (v1.size() == v2.size()) and std::equal(v1.begin(),v1.end(),v2.begin()); } template bool operator==(ValueArray const& v1, ValueArray const& v2) { typename ValueArray::size_type sz=v1.size(); return sz == v2.size() && 0==std::memcmp(v1.begin(),v2.begin(),sizeof(char)*sz); } template bool operator==(ValueArray const& v1, ValueArray const& v2) { typename ValueArray::size_type sz=v1.size(); return sz == v2.size() && 0==std::memcmp(v1.begin(),v2.begin(),sizeof(char)*sz); } template bool operator< (ValueArray const& v1, ValueArray const& v2) { return std::lexicographical_compare( v1.begin() , v1.end() , v2.begin() , v2.end() ); } template bool operator<(ValueArray const& v1, ValueArray const& v2) { typename ValueArray::size_type sz=v1.size(); return sz == v2.size() && std::memcmp(v1.begin(),v2.begin(),sizeof(char)*sz)<0; } template void memcpy(void *out,ValueArray const& v) { std::memcpy(out,v.begin(),v.size()*sizeof(T)); } #endif