2009-11-14 2 views
5

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

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

ответ

6

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

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

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

+2

Кроме этого * в теории * вы могли бы создать подкласс ublas :: vector, который сделал это. Ваш подкласс может вести себя как const ublas :: vector, который никогда не может быть изменен, или вам придется переопределить все методы, связанные с изменением размера контейнера, чтобы гарантировать, что не освободите память, которая не принадлежит ей , Только полный мазохист попытается это сделать. –

0

Я не думаю, что C++ допускает такое соглашение, как C.

+1

Конечно, да. Указатели - это всего лишь конкретный случай итераторов (с учетом указателя, с которым вы можете почтить *, и вы можете получить следующий элемент с ++. Это все, что вам нужно для инициализации std :: vector). –

4

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

vector<int> v(pv, pv+10); 
+0

Спасибо за ваш ответ, но это скопирует данные. Я хочу, чтобы 'v' и' pv' указывали на один и тот же блок данных. – dzhelil

+1

У вас этого не может быть. std :: vector всегда владеет своей памятью. Вы можете написать свой собственный векторный класс, хотя ... – shoosh

9

Я не уверен, как ваш вопрос относится к MATLAB/MEX, но обратите внимание на то, что MATLAB реализует код.

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

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

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

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

+11

Вы можете увидеть эти метаданные в формате настроек командного окна MATLAB с помощью 'format debug' – Mikhail

+0

классный трюк, спасибо за обмен – Amro

+0

Небольшой пункт о вашей диаграмме - вы делаете это выглядящим, как думал MATLAB создает новую копию данных, переназначает 'b', чтобы указать на него, и мутирует данные, на которые указывает' a'. На самом деле происходит то, что создается новая копия данных и * 'a' * переназначается для указания на нее, а затем новые данные мутируются. –

3

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

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); 
} 
2

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

  • Первый класс array_adaptor создает копию ваших данных, когда ublas :: vector вызывает конструктор копирования, а не очень полезный класс вообще. Я предпочел бы просто соответствующий конструктор для этого в классах unbounded_array или bounded_array.
  • Второй, mustow_array_adaptor, только держите ссылку на свои данные, поэтому вы можете использовать вектор для прямого изменения вашего массива 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 
2

Обычное предложение использовать неглубокую адаптер массив кажется вид саркастический мне - чтобы иметь возможность просто доступ массив через указатель, который вы, как предполагается, помещаете в shared_array со всей ссылочной подсчетой shebang (это ни к чему не приводит, поскольку вы не владеете массивом), а что еще более с кошмаром сглаживания данных. На самом деле 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); 

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

Смежные вопросы