2009-11-14 114 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]來初始化它,然後重新分配給外部數組。您可以將其更改爲受保護的,並直接在構造函數中分配給存儲。