Boost logo

Boost :

Subject: [boost] [uBLAS] array_adaptor, shallow_array_adaptor, interoperability and bugs
From: Guillermo Ruiz Troyano (ruiztroyano_at_[hidden])
Date: 2012-02-24 03:02:39


It is my first post. I still write worst in English than in C++. I hope don't mess it up…

INTEROPERABILITY

I'm been trying to use array_adaptor and shallow_array_adaptor to modify data with uBLAS. Yes, both classes are undocumented but it is useful (not this implementation) in some cases to have this kind of adaptor. For example:

// Statistics accumulator for regression, correlation, etc.
struct simple_regression {
   void operator()(double x, double y) { … }

   double mean_x;
   double var_x;
   double mean_y;
   double var_y;
   double covar;

   vector_wrapper<double> v() { … } // Return vector with a reference array adapter
};

Now, you need (for example) to interpolate two accumulators. Then you should to be able to do:

simple_regression sr, sr1, sr2;

double w = 0.5;
sr.v() = sr1.v()*w+sr2.v()*(1-w);

And you don't want that uBLAS copies data from you objects to inner containers continuously.

UNDOCUMENTED ATTEMPT

array_adaptor<T> has deep copy semantics. You can to construct an array_adaptor that use the external data, but vector call to copy constructor and this makes a copy:

struct point {
   double x;
   double y;
   double z;
};
point p = { 1, 2, 3 }
array_adaptor<double> a(3, &p.x); // Ok, a holds p address
vector<double, array_adaptor<double> > v(a); // v now holds a new address!

Well, a bit confusing class. If the only purpose of this class template is to pass plain data as arguments. Why not to put that constructor in unbounded_array and bounded_array?

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

Then, I found shallow_adaptor_array, more dangerous and funny. It has shallow copy semantics and a dummy shared_array with reference counting... ¿?

// Yes, I want troubles…
#define BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR

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

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; // Ok, now p = { 2, 4, 6 }
   v /= 2; // Ok, now p = { 1, 2, 3 }
   v = 2*v; // ¡¡¡What!!! p = { 1, 2, 3 }
} // Dummy functor of shared_array in shallow_array_adaptor called, ok.

When you assign v with an expression, this constructs a temporary and then swaps between the temporary and v. Here is the issue:

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
      }
   }
   ...
};

This is not a good idea. This member function should have a dual behavior:

class shallow_array_adaptor<T> : … {
   ...
   // Swapping
   BOOST_UBLAS_INLINE
   void swap (array_adaptor& a) {
      if (own_ && a.own_) { // If they own the data do wife-swapping
         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_); // Childs is another thing
   }
   ...
};

Modifying 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 = 2*v; // Ok, now p = { 2, 4, 6 } and v holds p address
} // 0kills of dynamic memory

WHY NOT?

1. An unbounded_array and bounded_array constructor for C plain arrays and copy semantics.
2. To discontinue this adaptor_array (with a #ifndef) or modify. Its behavior can do it point nº1.
3. To fix, test and document shallow_array_adaptor (or another name). It should be useful.

Yes, I know about memory leaks. But there are things like vector proxies, iterator ranges, etc. The problem comes when you have not a clear semantic.

Regards,
Guillermo Ruiz Troyano.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk