Инициализация вектора убласа из массива C - PullRequest
6 голосов
/ 15 ноября 2009

Я пишу расширение Matlab, используя библиотеку ublas C ++, и я хотел бы иметь возможность инициализировать свои векторы ublas из массивов C, передаваемых интерпретатором Matlab. Как я могу инициализировать вектор ublas из массива C без (ради эффективности) явного копирования данных. Я ищу что-то по следующим строкам кода:

using namespace boost::numeric::ublas;

int pv[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
vector<int> v (pv);

В общем, возможно ли инициализировать C ++ std::vector из массива? Как то так:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int pv[4] = { 4, 4, 4, 4};
    vector<int> v (pv, pv+4);

    pv[0] = 0;
    cout << "v[0]=" << v[0] << " " << "pv[0]=" << pv[0] << endl;

    return 0;
}

но там, где инициализация не скопирует данные. В этом случае на выходе получается

v[0]=4 pv[0]=0

но я хочу, чтобы вывод был таким же, где обновление массива C изменяет данные, на которые указывает вектор C ++

v[0]=0 pv[0]=0

Ответы [ 6 ]

8 голосов
/ 15 ноября 2009

Я не уверен, как ваш вопрос относится к MATLAB / MEX, но примечание, вы можете узнать, что MATLAB реализует стратегию copy-on-write .

Это означает, что при копировании массива, например, фактически копируются только некоторые заголовки, а сами данные совместно используются двумя массивами. И как только один из них изменяется, копия данных фактически создается.

Ниже приводится пример того, что может происходить под капотом (заимствовано из этого старого сообщения ):

-----------------------------------------
>> a = [35.7 100.2 1.2e7];

 mxArray a
    pdata -----> 35.7 100.2 1.2e7
  crosslink=0

-----------------------------------------
>> b = a;

 mxArray a
    pdata -----> 35.7 100.2 1.2e7
  crosslink     / \
    |  / \       |
    |   |        |
    |   |        |
   \ /  |        |
   crosslink     |
 mxArray b       |
    pdata --------

-----------------------------------------
>> a(1) = 1;

mxArray a
    pdata -----> (1) 100.2 1.2e7
  crosslink=0


   crosslink=0
 mxArray b
    pdata ------> 35.7 100.2 1.2e7 ...

Я знаю, что это на самом деле не отвечает на ваш вопрос, я просто подумал, что вы могли бы найти эту концепцию полезной.

6 голосов
/ 15 ноября 2009

Оба std::vector и ublas::vector являются контейнерами. Весь смысл контейнеров заключается в управлении хранением и временем жизни содержащихся в них объектов. Вот почему, когда вы инициализируете их, они должны скопировать значения в свое хранилище.

Массивы C - это области памяти, фиксированные по размеру и расположению, поэтому по своей природе их значения можно поместить в контейнер только путем копирования.

Вы можете использовать массивы C в качестве входных данных для многих функций алгоритма, так что, возможно, вы можете сделать это, чтобы избежать первоначальной копии?

4 голосов
/ 28 февраля 2012

В uBLAS storage.hpp есть два недокументированных класса. Вы можете изменить класс хранения по умолчанию (unbounded_array) в ublas :: vector с помощью одного из них.

  • Первый класс, array_adaptor, создает копию ваших данных, когда ublas :: vector вызывает конструктор копирования, вообще не очень полезный класс. Я бы предпочел просто соответствующий конструктор, чтобы сделать это в классах unbounded_array или bounded_array.
  • Второй, shallow_array_adaptor , содержит только ссылку на ваши данные, поэтому вы можете использовать vector для прямого изменения массива C. К сожалению, в нем есть некоторые ошибки, когда вы присваиваете выражение, оно теряет исходный указатель данных. Но вы можете создать производный класс, который решит эту проблему.

Вот патч и пример:

// BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR must be defined before include vector.hpp
#define BOOST_UBLAS_SHALLOW_ARRAY_ADAPTOR

#include <boost/numeric/ublas/vector.hpp>
#include <algorithm>
#include <iostream>

// Derived class that fix base class bug. Same name, different namespace.    
template<typename T>
class shallow_array_adaptor
: public boost::numeric::ublas::shallow_array_adaptor<T>
{
public:
   typedef boost::numeric::ublas::shallow_array_adaptor<T> base_type;
   typedef typename base_type::size_type                   size_type;
   typedef typename base_type::pointer                     pointer;

   shallow_array_adaptor(size_type n) : base_type(n) {}
   shallow_array_adaptor(size_type n, pointer data) : base_type(n,data) {}
   shallow_array_adaptor(const shallow_array_adaptor& c) : base_type(c) {}

   // This function must swap the values ​​of the items, not the data pointers.
   void swap(shallow_array_adaptor& a) {
      if (base_type::begin() != a.begin())
         std::swap_ranges(base_type::begin(), base_type::end(), a.begin());
   }
};

void test() {
    using namespace boost::numeric;
    typedef ublas::vector<double,shallow_array_adaptor<double> > vector_adaptor;

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

    point p = { 1, 2, 3 };
    vector_adaptor v(shallow_array_adaptor<double>(3, &p.x));

    std::cout << p.x << ' ' << p.y << ' ' << p.z << std::endl;
    v += v*2.0;
    std::cout << p.x << ' ' << p.y << ' ' << p.z << std::endl;
}

Выход:

1 2 3
3 6 9
4 голосов
/ 15 ноября 2009

Вы можете легко инициализировать std :: vector из массива C:

vector<int> v(pv, pv+10);
3 голосов
/ 24 июня 2012

Обычное предложение использовать адаптер с мелкими массивами кажется мне саркастичным - чтобы иметь возможность просто получить доступ к массиву через указатель, вы должны поместить его в shared_array со всеми шебангами для подсчета ссылок (которые ни к чему не приводят) , поскольку у вас нет массива) и, что более важно, с кошмаром псевдонимов данных. На самом деле, uBLAS имеет полноценную реализацию хранилища (array_adaptor), которая позволяет использовать векторы с внешними массивами c. Единственный улов - векторный конструктор, который делает копию. Почему эта замечательная функция не используется в библиотеке, мне совершенно не понятно, но в любом случае мы можем использовать небольшое расширение (на самом деле это 2 строки кода, окруженные обычным раздутием c ++)

template<class T>
class extarray_vector :
    public vector<T, array_adaptor<T> >
{
    typedef vector<T, array_adaptor<T> > vector_type;
public:
    BOOST_UBLAS_INLINE
    extarray_vector(size_type size, pointer p)
    { data().resize(size, p); }

    template <size_type N>
    BOOST_UBLAS_INLINE
    extarray_vector(T (&a)[N])
    { data().resize(N, a); }

    template<class V>
    BOOST_UBLAS_INLINE
    extarray_vector& operator = (const vector<T, V>& v)
    {
        vector_type::operator = (v);
        return *this;
    }

    template<class VC>
    BOOST_UBLAS_INLINE
    extarray_vector& operator = (const vector_container<VC>& v)
    {
        vector_type::operator = (v);
        return *this;
    }

    template<class VE>
    BOOST_UBLAS_INLINE
    extarray_vector& operator = (const vector_expression<VE>& ae)
    {
        vector_type::operator = (ae);
        return *this;
    }
};

вы можете использовать его так:

int i[] = {1, 4, 9, 16, 25, 36, 49};
extarray_vector<int> iv(i);
BOOST_ASSERT_MSG(i == &iv[0], "Vector should attach to external array\n");
iv[3] = 100;
BOOST_ASSERT(i[3] == 100);
iv.resize(iv.size() + 1, true);
BOOST_ASSERT_MSG(i != &iv[0], "And detach from the array on resize\n");
iv[3] = 200;
BOOST_ASSERT(i[3] == 100);
iv.data().resize(7, i, 0);
BOOST_ASSERT_MSG(i == &iv[0], "And attach back to the array\n");
BOOST_ASSERT(i[3] == 200);

Вы можете динамически присоединять и отсоединять вектор к внешнему хранилищу с помощью метода resize array_adaptor (сохранение или удаление данных). При изменении размера он автоматически отсоединяется от хранилища и становится обычным вектором. Присвоение из контейнеров отправляется непосредственно в хранилище, но присваивание из выражения выполняется через временное хранилище, а вектор отсоединяется от хранилища, используйте noalias(), чтобы предотвратить это. В конструкторе есть небольшие накладные расходы, так как data_ является приватным членом, и мы должны по умолчанию инициализировать его с новым T [0], а затем переназначить во внешний массив. Вы можете изменить его на защищенный и назначить для хранения непосредственно в конструкторе.

3 голосов
/ 06 января 2012

Вот пара функций для синтаксически удобного назначения (по общему признанию, не инициализация):

vector<int> v;
setVector(v, 3, 
          1, 2, 3);

matrix<int> m;
setMatrix(m, 3, 4,
            1,   2,   3,   4,
           11,  22,  33,  44,
          111, 222, 333, 444);

Функции:

/**
 * Resize a ublas vector and set its elements
 */
template <class T> void setVector(vector<T> &v, int n, ...)
{
    va_list ap;
    va_start(ap, n);
    v.resize(n);
    for (int i = 0; i < n; i++) {
        v[i] = va_arg(ap, T);
    }
    va_end(ap);
}

/**
 * Resize a ublas matrix and set its elements
 */
template <class T> void setMatrix(matrix<T> &m, int rows, int cols ...)
{
    va_list ap;
    va_start(ap, cols);
    m.resize(rows, cols);
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            m(i, j) = va_arg(ap, T);
        }
    }
    va_end(ap);
}
...