I had the need for a functionality similar to shallow_array_adaptor. I wrote a quick and dirty solution based on ublas::unbounded_array. I call this storage class my_unbounded_array which can hold data with/without ownership. The implementation is as follows

// Storage class
template <class T>
class my_unbounded_array : public boost::numeric::ublas::storage_array<my_unbounded_array<T>> {
    private:
        typedef my_unbounded_array<T> self_type;
    public:
        typedef std::size_t size_type;
        typedef std::ptrdiff_t difference_type;
        typedef T value_type;
        typedef const T& const_reference;
        typedef T& reference;
        typedef const T* const_pointer;
        typedef T* pointer;
        typedef const_pointer const_iterator;
        typedef pointer iterator;
        typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
        typedef std::reverse_iterator<iterator> reverse_iterator;


        inline explicit my_unbounded_array() : own_(true), size_(0), data_(nullptr) {};

        inline explicit my_unbounded_array(size_type n) : own_(true), size_(n), data_(allocate(n)) {}

        // If bind is true then just copy the address of external data
        inline my_unbounded_array(size_type n, pointer arr, bool bind=false) : own_(!bind), size_(n), data_(nullptr) {
            if(bind) data_.reset(arr);
            else {
                data_.reset(allocate(n));
                std::uninitialized_copy(arr, arr+n, begin());
            }
        }

        inline my_unbounded_array(size_type n, const value_type& init) : own_(true), size_(n), data_(allocate(n)) {
            std::uninitialized_fill_n(begin(), n, init);
        }

        inline my_unbounded_array(const my_unbounded_array& rhs) : own_(true), size_(rhs.size_), data_(allocate(rhs.size_)) {
            std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
        }

        inline ~my_unbounded_array() {
            if(!own_) data_.release();
        }

        inline size_type size() const {return size_;}

        inline bool owner() const {return own_;}

        inline bool empty() const {return size_==0;}

        //! Bind to external data. Set ownership to false.
        inline void bind(size_type n, pointer arr) {
            if(!own_) data_.release();
            own_=false;
            size_=n;
            data_.reset(arr);
        }

        inline void resize(size_type n) {
            if(size_!=n) {
                if(!own_) {
                    data_.release();
                    throw("Cannot resize external data."); // Throw an appropriate exception here. This is just an example
                }
                data_.reset(allocate(n));
                size_=n;
            }
        }

        inline void resize(size_type n, value_type init) {
            if(size_!=n) {
                if(!own_) {
                    data_.release();
                    throw("Cannot resize external data."); // Throw an appropriate exception here. This is just an example
                }
                pointer tmp=nullptr;
                if(n) {
                    tmp=allocate(n);
                    if(size_<n) {
                        std::uninitialized_copy(begin(), end(), tmp);
                        std::uninitialized_fill(tmp+size_, tmp+n, init);
                    } else
                        std::uninitialized_copy(begin(), begin()+n, tmp);
                }
                size_=n;
                data_.reset(tmp);
            }
        }


        // Element access
        inline const_reference operator[](size_type i) const {
            return (data_.get())[i];
        }

        inline reference operator[](size_type i) {
            return (data_.get())[i];
        }

        // Assignment
        inline my_unbounded_array& operator=(const my_unbounded_array& rhs) {
            if(this!=&rhs) {
                resize(rhs.size_);
                std::copy(rhs.data_.get(), (rhs.data_.get())+rhs.size_, data_.get());
            }
            return *this;
        }

        inline my_unbounded_array& assign_temporary(my_unbounded_array& rhs) {
            swap(rhs);
            return *this;
        }

        inline void swap(my_unbounded_array& rhs) {
            if(!own_) {
                data_.release();
                throw("Cannot resize external data."); // Throw an appropriate exception here. This is just an example
            }
            if(this!=&rhs) {
                std::swap(size_, rhs.size_);
                std::swap(own_, rhs.own_);
                data_.swap(rhs.data_);
            }
        }

        friend inline void swap(my_unbounded_array& x, my_unbounded_array& y) {x.swap(y);}

        inline const_iterator begin() const {return data_.get();}

        inline const_iterator end() const {return data_.get()+size_;}

        inline iterator begin() {return data_.get();}

        inline iterator end() {return data_.get()+size_;}

        inline const_reverse_iterator rbegin() const {return const_reverse_iterator(end());}

        inline const_reverse_iterator rend() const {return const_reverse_iterator(begin());}

        inline reverse_iterator rbegin() {return const_reverse_iterator(end());}

        inline reverse_iterator rend() {return const_reverse_iterator(begin());}

    private:
        friend class boost::serialization::access;

        // Serialization
        template<class Archive>
        void serialize(Archive& ar, const unsigned int version) {
            boost::serialization::collection_size_type s(size_);
            ar& boost::serialization::make_nvp("size", s);
            if(Archive::is_loading::value) resize(s);
            ar& boost::serialization::make_array(data_.get(), s);
        }

        static inline pointer allocate(const size_t n) {
            try {
                return n>0 ? new T[n] : nullptr;
            } catch(std::bad_alloc& e) {
                // Do something here!!!!!!!!!!!!
            }
        }

        //! Data ownership. True if it owns the memory block.
        /*!If false then data is treated as external and not deleted by the destructor,
        * and any operation involving resizing is not allowed. */
        bool own_;
        size_type size_;
        std::unique_ptr<T> data_;
};
// End of class my_unbounded_array

// Usage example
// vector<double, my_unbounded_array<double>> x(10, 0.); // x is just like normal ublas vector
// x.bind(10, ptr); // x is now holding external data pointed by raw pointer (ptr)

I hope this helps.

Sincerely,
Savyasachi Singh
Univ. of Florida



On Tue, Feb 28, 2012 at 12:00 PM, <ublas-request@lists.boost.org> wrote:
Send ublas mailing list submissions to
       ublas@lists.boost.org

To subscribe or unsubscribe via the World Wide Web, visit
       http://lists.boost.org/mailman/listinfo.cgi/ublas
or, via email, send a message with subject or body 'help' to
       ublas-request@lists.boost.org

You can reach the person managing the list at
       ublas-owner@lists.boost.org

When replying, please edit your Subject line so it is more specific
than "Re: Contents of ublas digest..."

Today's Topics:

  1. shallow_array_adaptor bug and fixes (Guillermo Ruiz Troyano)


---------- Forwarded message ----------
From: Guillermo Ruiz Troyano <ruiztroyano@gmail.com>
To: ublas@lists.boost.org
Cc: 
Date: Tue, 28 Feb 2012 14:10:41 +0100
Subject: [ublas] shallow_array_adaptor bug and fixes
I'm been trying to use shallow_array_adaptor as storage with ublas::vector to modify external data. When you try this:

#define BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR
#include <boost/numeric/ublas/vector.hpp>

struct point {
  double x;
  double y;
  double z;
};

void test() {
  using namespace boost::numeric::ublas;

  point p = { 1, 2, 3 }
  shallow_array_adaptor<double> a(3, &p.x);            // Ok, a holds p address
  vector<double, shallow_array_adaptor<double> > v(a); // Ok, v holds p address

  v += v;     // Ok, now p = { 2, 4, 6 }
  v /= 2;     // Ok, now p = { 1, 2, 3 }
  v = v*2;    // <- Oh no, p = { 1, 2, 3 } !!!
}

The bug comes from swap function of shallow_array_adapter that is used by vector. When you assign v with an expression, this constructs a temporary and then swaps with it. The issue is that it swaps pointers without check if both objects own the data:

class shallow_array_adaptor<T> : … {
  ...
  // Swapping
  BOOST_UBLAS_INLINE
  void swap (shallow_array_adaptor &a) {
     if (this != &a) {
        std::swap (size_, a.size_);
        std::swap (own_, a.own_);
        std::swap (data_, a.data_); // This is definitely infidelity
     }
  }
  ...
};

When both are data owners can do that (as unbounded_array does). But if they are not then it should swap as bounded_array does. Something like this:

class shallow_array_adaptor<T> : … {
  ...
  // Swapping
  BOOST_UBLAS_INLINE
  void swap (array_adaptor& a) {
     if (own_ && a.own_) {
        if (this != &a) {
           std::swap(size_, a.size_);
           std::swap(own_, a.own_);
           std::swap(data_, a.data_);
        }
     }
     else if (&data_[0] != &a.data_[0])
        std::swap_ranges(data_, data_+size_, a.data_);
  }
  ...
};

Fixing this function I get:

void test() {
  point p = { 1, 2, 3 }
  shallow_array_adaptor<double> a(3, &p.x);            // Ok, a holds p address
  vector<double, shallow_array_adaptor<double> > v(a); // Ok, v holds p address
  v = v*2;    // <- Ok, now p = { 2, 4, 6 } and v holds p address!
}

Sometimes you need something like shallow_array_adaptor. On the other hand, array_adaptor is a redundant class that unbounded_array or bounded_array can do if they have a constructor like this (or with a pair of iterators as parameters):

unbounded_array(size_t size, T* data)
: size_(size), data_(new value_type[size]) {
  std::copy(data, data+size, data_);
}

I would like to see:
1. A fixed shallow_array_adaptor or a similar storage class that references external data.
2. unbounded_array and bounded_array constructor that you pass an iterator range for copy.  adaptor_array is to use a sledgehammer to crack a nut!

Regards,
Guillermo Ruiz Troyano

_______________________________________________
ublas mailing list
ublas@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/ublas