【问题标题】:Initializing a ublas vector from a C array从 C 数组初始化 ublas 向量
【发布时间】:2009-11-14 22:33:23
【问题描述】:

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

【问题讨论】:

    标签: c++ matlab vector ublas


    【解决方案1】:

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

    • 当 ublas::vector 调用复制构造函数时,第一个类 array_adaptor 会复制您的数据,这根本不是很有用的类。我宁愿简单地在 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】:

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

        • 除了理论上,您可以创建一个 ublas::vector 的子类来执行此操作。您的子类可能表现为永远无法调整大小的 const ublas::vector,或者您必须覆盖调整容器大小所涉及的所有方法,以确保不会释放不属于它的内存.只有完全的受虐狂才会尝试这样做。
        【解决方案4】:

        我不确定您的问题与 MATLAB/MEX 有何关系,但附带说明一下,您可能想知道 MATLAB 实现了 copy-on-write 策略。

        这意味着,例如,当您复制一个数组时,实际上只复制了一些标头,而数据本身在两个数组之间共享。一旦其中一个被修改,数据的副本实际上就被复制了。

        以下是引擎盖下可能发生的事情的模拟(借用此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 ...
        

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

        【讨论】:

        • 您可以通过format debugformat debug在 MATLAB 命令行设置格式中查看此元数据
        • 关于您的图表的一个小问题 - 您让它看起来像 MATLAB 创建数据的新副本,重新分配 b 以指向它,并改变 a 指向的数据.实际发生的是创建数据的新副本并重新分配 a 以指向它,然后对新数据进行变异。
        • 这与问题几乎没有关系。问题是关于 C++ 的。如果您使用 matlab 类,它们可能会针对冗余副本进行一些编译时优化。一旦您从中获取原始指针,matlab 就无法阻止其他库尝试进行无用的复制。事实上,请求原始指针的操作会触发 matlab 复制输入参数。
        • @Dimitry downvote 足够公平,因为它没有回答问题,即使它有点相关,我仍然会保留这个 9 哟答案......至于你的最后一句话,我应该纠正你并说在 MEX-API 级别,当您请求数字数组的原始数据(即mxGetData 等)时,MATLAB 不会创建副本。当然,这并不能阻止您通过将原始指针包装在 std::vector 中来制作副本。
        • 好吧,老实说,我的经验来自 Octave,无论如何,谷歌倾向于重定向到 matlab 答案。通过提供原始内存指针 Matlab\Octave 数组容器放弃了它们对内存访问的任何控制,为了保证使用该指针不会引起副作用,它们必须确保内存不被其他对象共享. Octave 在指针请求上执行此操作,除非您使用 const 修饰符对所有内容进行点缀。 Matlab mex 编译器可能会更精细并跟踪指针的使用情况,也可能不会。
        【解决方案5】:

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

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

        【讨论】:

        • 感谢您的回答,但这会复制数据。我希望vpv 指向同一个数据块。
        • 你不能这样。 std::vector 总是拥有它的内存。您可以编写自己的矢量类...
        【解决方案6】:

        这里有几个函数用于语法上方便的赋值(当然不是初始化):

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

        【讨论】:

          猜你喜欢
          • 2014-04-21
          • 2022-12-31
          • 2015-01-05
          • 1970-01-01
          • 1970-01-01
          • 2021-10-02
          • 2012-06-21
          • 2013-05-10
          • 1970-01-01
          相关资源
          最近更新 更多