正文

我写的多维数组auto_array[续]2008-02-19 19:00:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/rickone/32766.html

分享到:

实现生成器,语法更简洁:

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 allocator
template<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 interfaces
template<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

阅读(5047) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册