Инициализация вектора ublas из массива C
Я пишу расширение Matlab, используя библиотеку C++ ublas, и я хотел бы иметь возможность инициализировать мои векторы 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 ответов
и std::vector
и ublas::vector
контейнеры. Весь смысл контейнеров заключается в управлении хранением и временем жизни содержащихся в них объектов. Вот почему при инициализации они должны копировать значения в собственное хранилище.
массивы C-это области памяти, фиксированные по размеру и местоположению, поэтому по своей природе вы можете получить их значения только в контейнер путем копирования.
вы можете использовать массивы C в качестве входных данных для многих функций алгоритма, поэтому, возможно, вы можете сделать это избежать первоначальной копии?
Я не уверен, как ваш вопрос относится к MATLAB / MEX, но Примечание, Вы можете знать, что MATLAB реализует копирование при записи стратегии.
это означает, что при копировании массива, например, копируются только некоторые заголовки, а сами данные совместно используются между двумя массивами. И как только один из них будет изменен, копия данных будет фактически сделана.
ниже приведено моделирование того, что может происходить под капотом (заимствовано из этого старый пост):
-----------------------------------------
>> 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 ...
Я знаю, что это не отвечает на ваш вопрос, я просто подумал, что вы можете найти концепцию полезной.
в хранилище uBLAS есть два недокументированных класса.ТЭЦ. Вы можете изменить класс хранения по умолчанию (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
вот пара функций для синтаксически удобного назначения (по общему признанию, не инициализации):
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);
}
обычное предложение использовать shallow array adaptor кажется мне саркастичным - иметь возможность просто получить доступ к массиву через указатель, который вы должны поместить в shared_array со всеми ссылками, подсчитывающими shebang (это ни к чему, так как вы не владеете массивом), и более того, с кошмаром сглаживания данных.
Собственно, uBLAS имеет полноценную реализацию хранилища (array_adaptor
), который позволяет использовать векторы с внешними массивами C. Единственный улов-вектор конструктор, который делает копию. Почему эта приятная функция не используется в библиотеке, мне совершенно непонятно, но в любом случае мы можем использовать небольшое расширение (на самом деле это 2 строки кода, окруженные обычным c++ bloat)
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], а затем переназначить внешний массив. Вы можете изменить его на защищенный и назначить хранилище непосредственно в конструкторе.