6

我正在使用 C++ ublas 库编写 Matlab 扩展,我希望能够从 Matlab 解释器传递的 C 数组中初始化我的 ublas 向量。如何在不(为了提高效率)显式复制数据的情况下从 C 数组初始化 ublas 向量。我正在寻找以下代码行的内容:

using namespace boost::numeric::ublas;

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

一般来说,是否可以std::vector从数组初始化 C++?像这样的东西:

#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
4

6 回答 6

8

我不确定您的问题与 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 ...

我知道这并不能真正回答您的问题,我只是认为您可能会发现这个概念很有帮助。

于 2009-11-14T23:39:02.303 回答
6

两者std::vector都是ublas::vector容器。容器的全部意义在于管理其所包含对象的存储和生命周期。这就是为什么当您初始化它们时,它们必须将值复制到它们拥有的存储中。

C 数组是大小和位置固定的内存区域,因此就其性质而言,您只能通过复制将其值放入容器中。

您可以使用 C 数组作为许多算法函数的输入,所以也许您可以这样做来避免初始副本?

于 2009-11-14T23:01:14.813 回答
4

您可以轻松地从 C 数组初始化 std::vector:

vector<int> v(pv, pv+10);
于 2009-11-14T22:38:14.470 回答
4

uBLAS storage.hpp 中有两个未记录的类。您可以使用其中之一更改 ublas::vector 中的默认存储类 (unbounded_array)。

  • 第一个类 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
于 2012-02-28T06:44:45.160 回答
3

使用浅数组适配器的通常建议对我来说似乎有点讽刺——能够通过一个指针简单地访问一个数组,你应该将它放入一个带有所有引用计数 shebang 的 shared_array 中(这毫无意义,因为你不拥有数组),更重要的是数据混叠的噩梦。实际上,uBLAS 有一个成熟的 storage ( 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 的 resize 方法(保留或丢弃数据)动态地将向量附加和分离到外部存储。在调整大小时,它会自动从存储中分离并成为常规向量。容器的赋值直接进入存储,但表达式的赋值是通过临时完成的,并且向量从存储中分离,用noalias()​​于防止这种情况。由于 data_ 是私有成员,因此构造函数的开销很小,我们必须默认使用 new T[0] 对其进行初始化,然后重新分配给外部数组。您可以将其更改为受保护并直接在构造函数中分配给存储。

于 2012-06-23T22:05:18.557 回答
2

以下是一些语法上方便赋值的函数(诚然不是初始化):

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);
}
于 2012-01-06T02:52:09.853 回答