2009-11-14 113 views
5

我正在使用C++ ublas库编写Matlab扩展,并且希望能够通过由Matlab中间人传递的C数组初始化我的ublas向量。 如何从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); 

一般情况下,是有可能从一个数组初始化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::vectorublas::vector是容器。容器的要点是管理其容纳对象的存储和生命周期。这就是为什么初始化它们时,它们必须将值复制到它们拥有的存储中。

C数组是指在大小和位置上固定的内存区域,因此它们的性质只能通过复制将其值存入容器。

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

+2

除了*在理论上*你可以创建一个ublas :: vector的子类来做到这一点。你的子类可以像一个const ublas :: vector一样不可调整大小,或者你必须覆盖调整容器大小所涉及的所有方法,以确保不会释放不属于它的内存。 只有一个完整的受虐狂会尝试这一点。 – 2009-11-16 17:55:22

0

我不认为C++允许像C这样的约定。

+1

当然是的。 指针只是迭代器的一个特定情况(给定一个可以遵从的指针*,你可以用++来获得下一个项目,这就是你需要初始化一个std :: vector的所有东西)。 – 2009-11-14 22:45:37

4

可以从C数组容易初始化一个std ::向量:

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

感谢您的回答,但这会复制数据。我想'v'和'pv'指向同一块数据。 – dzhelil 2009-11-14 22:50:24

+1

你不能那样做。 std :: vector总是拥有它的内存。你可以编写你自己的矢量类,但是... – shoosh 2009-11-14 22:56:42

9

我不确定你的问题与MATLAB/MEX有什么关系,但有一个附注,你可能想知道MATLAB实现了一个复制策略。

这意味着当您复制数组时,实际上只复制了一些标题,而数据本身则在两个数组之间共享。一旦修改了其中的一个,实际上就会生成一份数据。

下面是什么可能引擎盖(从这个old post借来的)下发生的一切simluation:

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

你可以用'format debug'在MATLAB命令窗口设置格式中看到这个元数据。 – Mikhail 2010-08-07 09:25:37

+0

很酷的技巧,感谢分享 – Amro 2010-08-07 10:17:41

+0

关于你的图的一个小点 - 你认为MATLAB看起来像是创建了一个新的数据副本,重新指定'b'指向它,并且改变'a'指向的数据。实际发生的情况是创建一个新的数据副本,并且*'a' *被重新分配以指向它,然后新数据发生变化。 – 2015-03-03 09:09:47

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。您可以使用其中之一更改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 
2

通常建议使用浅阵列适配器似乎有点讥讽我的话 - 能够简单地访问通过一个指针的数组,你应该把它放到一个shared_array中,所有引用计数shebang(没有任何意义,因为你没有拥有数组),还有一个数据别名的噩梦。实际上,uBLAS有一个完整的存储实现(array_adaptor),允许使用具有外部c数组的向量。唯一的问题是制作副本的矢量构造函数。为什么这个不错的功能是不是在图书馆使用的是远远超出我的,但无论如何,我们可以用一个小的扩展(它实际上是两行代码与通常所包围的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]来初始化它,然后重新分配给外部数组。您可以将其更改为受保护的,并直接在构造函数中分配给存储。