实现生成器,语法更简洁: shape是一个全局唯一对象,用来生成多维数组的'外观',可以拿传统的C语法进行比较: 定义一个三维数组(传统): int a[3][4][5]; 一个动态三维数组: auto_array<int,3> a(shape[3][4][5]); 形式上很相似,如果你不进行特殊的操作,完全可以拿下面的a代替上面的.所不同的是,传统的[3][4][5]是需要在编译期确定的,而下面的可以是运行期的动态变量,所在的存储空间也不同,上面占用栈空间,下面的是在堆上,由指定的分配器优化分配的. 你如果随意乱用shape,比如 auto_array<int,3> a(shape[3][4]); 将会引发编译错误,同样的,对其它维度数和shape后面的[]数不同的形式都会报错.换句话说,shape的[]会返回不同的类型,这取决于你用了几次[]. shape还可以用来传给reshape,形式是 a.reshape(shape[3][4][5]); 它不能再继续加括号指定范围了,他不能指定起始下标,如果希望不从0开始下标数组,只能使用另一种形式的reshape,这种形式值得再说明一下. reshape(dim0,width0,start0)(dim1,width1,start1)(...)...; 为了防止空间的重复无用的分配,这里的reshape,在调用operator()的时候,并没有真的去'分配'它,只是设定了相应的维度宽度等值.而需要真正分配空间,可以几种方法: 1,调用一次operator[],哪怕没任何用. 2,在reshape语句末尾,加上一句now(),如 a.reshape(0,4)(1,5).now(); 则会马上发生分配.有没有真的分配,会影响到其它和容器相关的函数,包括: safe_bool转型,可以这样使用: if(a) cout<<"a is not empty!"<<endl; a可以自动转型,但这是安全的,你不能指望这样的代码通过编译: bool b=a; 或者其它形式的头疼地隐式转换: a && true; [这种安全转型是从boost里学的;)] 此外还有一些标准的迭代器函数,begin(),end()等,另外还有一个operator&,用来返回数组的首地址,这和C语言兼容: int *add=&a; 但是,推荐少用,因为&a会发生改变,因此add不能存起来过后使用. 最后,数组不能直接拷贝,但是可以拷贝构造.当然你可以使用算法copy,那是面向迭代器,而迭代器的引用只要元素能拷贝就能完成任务,阻止拷贝的目的是,防止一个书写不当,引起整体的费时地拷贝. [其它可能还有一些想法,再陆续加进来] 最新代码: #ifndef AUTO_ARRAY#define AUTO_ARRAY #include <stdexcept> //test allocatortemplate<typename T>class _test_allocator{public: static void* allocate(std::size_t n) { return malloc(n*sizeof(T)); } static void deallocate(void* p, std::size_t /* __n */) { free(p); } static void* reallocate(void* __p, std::size_t /* old_sz */, std::size_t __new_sz) { return realloc(__p, __new_sz*sizeof(T)); }}; //implemention of auto_array internal struct range_list{ long start; std::size_t width; std::size_t step;}; template<typename T, std::size_t dim, typename Allocator = _test_allocator<T> >struct auto_array_impl{ auto_array_impl() : range_changed(false), data(0), length(0), max_sz(0) {} auto_array_impl(const auto_array_impl& r) : range_changed(r.range_changed), data(0), length(r.length), max_sz(r.max_sz) { // deep copy if(r.empty()) return; data=allocate(r.max_sz); for(std::size_t i=0;i<r.max_sz;++i) data[i]=r.data[i]; for(std::size_t i=0;i<dim;++i) rl[i]=r.rl[i]; } virtual ~auto_array_impl() { clear(); } T* allocate(std::size_t n) { return static_cast<T*>(Allocator::allocate(n)); } void deallocate(T* p, std::size_t sz) { Allocator::deallocate(static_cast<void*>(p),sz); } T* reallocate(T* p, std::size_t _old_sz, std::size_t _new_sz) { return static_cast<T*>( Allocator::reallocate(static_cast<void*>(p),_old_sz,_new_sz) ); } bool getready(); void clear(); range_list rl[dim]; bool range_changed; T* data; std::size_t length; std::size_t max_sz;}; template<typename T, std::size_t dim, typename Allocator>bool auto_array_impl<T,dim,Allocator>::getready(){ if(range_changed) { std::size_t _new_length=1; for(long i=dim-1;i>=0;--i) { _new_length*=rl[i].width; rl[i].step = (i+1==dim?1:rl[i+1].step*rl[i+1].width); } if(_new_length>max_sz) { do{ max_sz = (max_sz==0?128:(max_sz<<1)); }while(_new_length>max_sz); if(data) data=reallocate(data,0,max_sz); else data=allocate(max_sz); } length=_new_length; range_changed=false; } return length>0;} template<typename T, std::size_t dim, typename Allocator>void auto_array_impl<T,dim,Allocator>::clear(){ if(data) { deallocate(data,max_sz); data=0; max_sz=0; }} //generator of auto_array ; use global object 'shape'template<std::size_t dim>class auto_array_gen{public: auto_array_gen(const auto_array_gen<dim-1>& pre, std::size_t width) : pre_gen(pre), cur_width(width) {} auto_array_gen<dim+1> operator[](std::size_t width) const { return auto_array_gen<dim+1>(*this,width); } void set_range(range_list* rl) const { rl[dim-1].start=0; rl[dim-1].width=cur_width; pre_gen.set_range(rl); }private: auto_array_gen<dim-1> pre_gen; std::size_t cur_width;}; template<>class auto_array_gen<1>{public: explicit auto_array_gen(std::size_t width) : cur_width(width) {} auto_array_gen<2> operator[](std::size_t width) const { return auto_array_gen<2>(*this,width); } void set_range(range_list* rl) const { rl[0].start=0; rl[0].width=cur_width; }private: std::size_t cur_width;}; template<>class auto_array_gen<0>{public: auto_array_gen<1> operator[](std::size_t width) const { return auto_array_gen<1>(/* *this, */width); // *this cannot be copy-constructed } static auto_array_gen* Instance() { if(0==instance) { instance = new auto_array_gen; } return instance; }private: auto_array_gen() {} auto_array_gen(auto_array_gen&) {} static auto_array_gen *instance;}; auto_array_gen<0>* auto_array_gen<0>::instance = 0; auto_array_gen<0>& shape=*auto_array_gen<0>::Instance(); //auto_array interfacestemplate<typename T, std::size_t dim, typename Allocator = _test_allocator<T> >class auto_array{public: typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type* iterator; typedef const value_type* const_iterator; typedef value_type& reference; typedef const value_type& const_reference; typedef std::size_t size_type; typedef ptrdiff_t difference_type; virtual ~auto_array() { delete ptimpl; } auto_array() { ptimpl=new auto_array_impl<T,dim,Allocator>; for(std::size_t i=0;i<dim;++i) ptimpl->rl[i].width=0; } auto_array(const auto_array& r) { ptimpl=new auto_array_impl<T,dim,Allocator>(*r.ptimpl); } explicit auto_array(const auto_array_gen<dim>& gen) { ptimpl=new auto_array_impl<T,dim,Allocator>; ptimpl->range_changed=true; gen.set_range(ptimpl->rl); ptimpl->getready(); } template<typename _T, std::size_t _dim, typename _Allocator> class reshape_functor { public: reshape_functor(auto_array_impl<_T,_dim,_Allocator>* _ptimpl) : ptimpl(_ptimpl) {} reshape_functor operator()(std::size_t dim_index, std::size_t width, long start = 0) { if(dim_index>=_dim) throw(std::runtime_error("array reshape error: dimensionality too big")); ptimpl->rl[dim_index].start=start; ptimpl->rl[dim_index].width=width; return *this; } bool now() { return ptimpl->getready(); } private: auto_array_impl<_T,_dim,_Allocator>* ptimpl; }; reshape_functor<T,dim,Allocator> reshape(std::size_t dim_index, std::size_t width, long start = 0) { ptimpl->range_changed=true; return reshape_functor<T,dim,Allocator>(ptimpl)(dim_index,width,start); } void reshape(const auto_array_gen<dim>& gen) { ptimpl->range_changed=true; gen.set_range(ptimpl->rl); ptimpl->getready(); } template<typename _T, std::size_t _dim> class index_functor { public: index_functor(_T *pdata, const range_list *prl) : data(pdata), rl(prl) {} index_functor<_T,_dim-1> operator[](long index) { if(index>=rl[0].start && index-rl[0].start<static_cast<long>(rl[0].width)) return index_functor<_T,_dim-1>(data+(index-rl[0].start)*rl[0].step,rl+1); else throw(std::runtime_error("array access error: out of bound")); } private: _T *data; const range_list *rl; }; template<typename _T> class index_functor<_T,0> { public: index_functor(_T *pdata, const range_list *prl) : data(pdata) {} operator _T(){ return data[0]; } template<typename U> _T& operator=(const U &other){ return data[0]=other; } template<typename U> _T& operator+=(const U &other){ return data[0]+=other; } template<typename U> _T& operator-=(const U &other){ return data[0]-=other; } template<typename U> _T& operator*=(const U &other){ return data[0]*=other; } template<typename U> _T& operator/=(const U &other){ return data[0]/=other; } template<typename U> _T& operator%=(const U &other){ return data[0]%=other; } template<typename U> _T& operator<<=(const U &other){ return data[0]<<=other; } template<typename U> _T& operator>>=(const U &other){ return data[0]>>=other; } template<typename U> _T& operator^=(const U &other){ return data[0]^=other; } template<typename U> _T& operator&=(const U &other){ return data[0]&=other; } template<typename U> _T& operator|=(const U &other){ return data[0]|=other; } private: _T *data; }; index_functor<T,dim-1> operator[](long index) { if(ptimpl->getready()) return index_functor<T,dim>(ptimpl->data,ptimpl->rl)[index]; else throw(std::runtime_error("array access error: shape uninitialized")); } struct dummy{void nonnull(void){}}; typedef void (dummy::*safe_bool)(void); operator safe_bool() {return ptimpl->data==0?0:&dummy::nonnull;} bool empty() const {return ptimpl->data==0;} size_type size() const { return ptimpl->length; } size_type max_size() const { return ptimpl->max_sz; } void clear() { ptimpl->clear(); } void operator=(int zero){ ptimpl->clear(); } iterator begin() { return ptimpl->data; } const_iterator begin() const { return ptimpl->data; } iterator end() { return ptimpl->data+ptimpl->length; } const_iterator end() const { return ptimpl->data+ptimpl->length; } void swap(auto_array& r) { using std::swap; swap(ptimpl,r.ptimpl); } iterator operator&() { return begin(); } private: void operator=(const auto_array<T,dim,Allocator>&); auto_array_impl<T,dim,Allocator>* ptimpl;}; #endif rickone 2008/02/19

评论