从 C 数组初始化 ublas 向量

发布于 2024-08-11 08:13:48 字数 796 浏览 11 评论 0原文

我正在使用 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);

一般来说,是否可以从数组初始化 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

I am writing a Matlab extension using the C++ ublas library, and I would like to be able to initialize my ublas vectors from the C arrays passed by the Matlab interpeter.
How can I initialize the ublas vector from a C array without (for the sake of efficiency) explicitly copying the data. I am looking for something along the following lines of code:

using namespace boost::numeric::ublas;

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

In general, is it possible to initialize a C++ std::vector from an array? Something like this:

#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;
}

but where the initialization would not copy the data. In this case the output is

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

but I want the output to be the same, where updating the C array changes the data pointed to by the C++ vector

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

泪眸﹌ 2024-08-18 08:13:49

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

  • 第一个类 array_adaptor 在 ublas::vector 调用复制构造函数时复制数据,这根本不是非常有用的类。我宁愿简单地使用适当的构造函数在 unbounded_array 或有界_array 类中执行此操作。
  • 第二个,shallow_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

There are two undocumented classes in uBLAS storage.hpp. You can change the default storage class (unbounded_array) in ublas::vector with one of these.

  • The first class, array_adaptor, makes a copy of your data when ublas::vector calls to copy constructor, not very useful class at all. I would rather simply the appropriate constructor to do this in unbounded_array or bounded_array classes.
  • The second, shallow_array_adaptor, only hold a reference of your data, so you can use vector to directly modify your C array. Unfortunately, it has some bugs, when you assign an expression it losses the original data pointer. But you can create a derived class that fix this problem.

Here the patch and an example:

// 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;
}

Output:

1 2 3
3 6 9
时光沙漏 2024-08-18 08:13:49

使用浅数组适配器的通常建议对我来说似乎有点讽刺 - 为了能够简单地通过指针访问数组,您应该将其放入带有所有引用计数的共享数组中(这没有任何结果,因为您不拥有该数组),更重要的是数据别名的噩梦。
实际上,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_ 是私有成员,因此构造函数中的开销很小,我们必须默认使用 new T[0] 初始化它,然后重新分配给外部数组。您可以将其更改为受保护并直接在构造函数中分配给存储。

The usual suggestion to use shallow array adaptor seems kind of sarcastic to me - to be able to simply access an array through a pointer you're supposed to put it into a shared_array with all the reference counting shebang (that comes to nothing, since you don't own the array) and what's more with a nightmare of data-aliasing.
Actually, uBLAS has a fully-fledged implementation of storage (array_adaptor) which allows to use vectors with external c arrays. The only catch is vector constructor which makes a copy. Why this nice feature is not used in the library is quite beyond me, but anyway, we can use a little extension (it's actually 2 lines of code surrounded with usual 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;
    }
};

you can use it like 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);

You can dynamically attach and detach vector to external storage via array_adaptor's resize method (keeping or discarding data). On resize it detaches from storage automatically and becomes regular vector. Assignment from containers goes directly into storage, but assignment from expression is done via a temporary and vector is detached from storage, use noalias() to prevent that. There's a small overhead in constructor since data_ is private member and we have to default initialize it with new T[0], then reassign to external array. You may change it to protected and assign to storage directly in the constructor.

枕梦 2024-08-18 08:13:49

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

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

Here are a couple of functions for syntactically convenient assignment (admittedly not initialization):

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

The functions:

/**
 * 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);
}
伤痕我心 2024-08-18 08:13:48

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

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

I'm not sure how your question relates to MATLAB/MEX, but a side note, you might want to know that MATLAB implements a copy-on-write strategy.

This means that when you copy an array for example, only some headers are actually copied, while the data itself is shared between the two arrays. And once one of them is modified, a copy of the data is actually made.

The following is a simluation of what might be happening under the hood (borrowed from this 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 ...

I know this doesn't really answer your question, I just thought you might find the concept helpful.

沫尐诺 2024-08-18 08:13:48

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

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

您可以使用 C 数组作为许多算法函数的输入,因此也许您可以这样做以避免初始复制?

Both std::vector and ublas::vector are containers. The whole point of containers is to manage the storage and lifetimes of their contained objects. This is why when you initialize them they must copy values into storage that they own.

C arrays are areas of memory fixed in size and location so by their nature you can only get their values into a container by copying.

You can use C arrays as the input to many algorithm functions so perhaps you can do that to avoid the initial copy?

末蓝 2024-08-18 08:13:48

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

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

You can initialize a std::vector from a C array easily:

vector<int> v(pv, pv+10);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文