0%

Map类实现与C/C++数组的转换

本章介绍如何使用 Map 类通过不复制数据的方法将原始 C/C++ 数组转换成矩阵或向量

Map类模板参数与构造函数

Map类模板参数如下:

1
Map<typename MatrixType, int MapOptions = Unaligned, typename StrideType>
  • MatrixType : required ,声明是哪种类型的矩阵或向量

  • MapOptions : optional ,声明指针是对齐的还是未对齐的

  • StrideType : optional ,使用 Stride为内存中的数组自定义布局

    Stride 类有两个模板参数:

    1
    Stride<OuterStride, InnerStride>
    • OuterStride : 指的是 列优先矩阵的两个连续列 或 行优先矩阵的两个连续行 之间的指针增量
    • InnerStride : 指的是 列优先矩阵的某一列内两个连续行 或 行优先矩阵的某一列内两个连续列 之间的指针增量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int array[8];
    for(int i = 0; i < 8; ++i) array[i] = i;
    // 默认列优先
    cout << "Column-major:\n"
    << Map<Matrix<int,2,4> >(array) << endl;
    // 通过定义矩阵类型实现行优先
    cout << "Row-major:\n"
    << Map<Matrix<int,2,4,RowMajor> >(array) << endl;
    // 通过Stride参数实现行优先
    cout << "Row-major using stride:\n"
    << Map<Matrix<int,2,4>, Unaligned, Stride<1,4> >(array) << endl;

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Column-major:
    0 2 4 6
    1 3 5 7
    Row-major:
    0 1 2 3
    4 5 6 7
    Row-major using stride:
    0 1 2 3
    4 5 6 7

    Stride 类还能更灵活:

    1
    2
    3
    4
    5
    6
    int array[24];
    for(int i = 0; i < 24; ++i) array[i] = i;
    cout << Map<MatrixXi, 0, Stride<Dynamic,2> >
    (array, 3, 3, Stride<Dynamic,2>(8, 2))
    << endl;
    // 列间隔8,行间隔2

    输出:

    1
    2
    3
    0  8 16
    2 10 18
    4 12 20

在构造一个Map类型对象的时候,还需要另外两个信息:指向数组内存空间的指针;目标矩阵/向量的大小。当矩阵/变量类型为固定大小的类型时,第二个信息可以省略。

  • 构造一个Map类型的对象用来转换动态大小的 float 矩阵:其中 pf 是一个 float* 类型的指针,指向数组内存空间,rowscols 则定义了目标矩阵的维度

    1
    Map<MatrixXf> mf(pf, rows, cols);
  • 构造一个Map类型的对象用来转换固定大小的 int 只读向量:其中 pi 是一个 int* 类型的指针,因为 Vector4i 已经说明了向量维度,所以不再需要向构造函数传入向量的维度信息

    1
    Map<const Vector4i> mi(pi);

需要注意的是,Map类并没有默认构造函数,在构造对象的时候,必须告诉构造函数所要转化的数组的地址指针。

Map类型对象的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef Matrix<float, 1, Dynamic> MatrixType;
typedef Map<MatrixType> MapType;
typedef Map<const MatrixType> MapTypeConst; // a read-only map
const int n_dims = 5;

MatrixType m1(n_dims), m2(n_dims);
m1.setRandom();
m2.setRandom();
float *p = &m2(0); // 拿到矩阵m2的地址
MapType m2map(p,m2.size()); // 矩阵m2map和矩阵m2共享内存空间(地址相同)
MapTypeConst m2mapconst(p, m2.size()); // 通过矩阵m2mapconst只能实现对m2的访问,而不能修改

cout << "m1: " << m1 << endl;
cout << "m2: " << m2 << endl;
cout << "Squared euclidean distance: " << (m1-m2).squaredNorm() << endl;
cout << "Squared euclidean distance, using map: "
<< (m1-m2map).squaredNorm() << endl;

m2map(3) = 7; // 这一步会同时改变m2和m2mapconst的值,因为这三者共享内存空间
cout << "Updated m2: " << m2 << endl;
cout << "m2 coefficient 2, constant accessor: " << m2mapconst(3) << endl;

输出:

1
2
3
4
5
6
m1:  0.680375 -0.211234  0.566198   0.59688  0.823295
m2: -0.604897 -0.329554 0.536459 -0.444451 0.10794
Squared euclidean distance: 3.26291
Squared euclidean distance, using map: 3.26291
Updated m2: -0.604897 -0.329554 0.536459 7 0.10794
m2 coefficient 2, constant accessor: 7

虽然Eigen的所有函数都会像接受其他Eigen类型一样接受Map对象,但在实现自己的函数过程中,从Map对象到其对应的Dense类对象的转换并不会自动完成。具体见 以Eigen类型为参数编写函数

修改映射数组

可以使用C++的 placement new 语法实现在生命Map对象后修改其数组。简而言之,placement new 语法实现的是从给定指针所指向的地址开始创建一个新的对象,一般来说该指针指向的是一片提前申请好的内存,所以也不会进行内存分配。例如:

1
2
3
4
5
int data[] = {1,2,3,4,5,6,7,8,9};
Map<RowVectorXi> v(data,4);
cout << "The mapped vector v is: " << v << "\n";
new (&v) Map<RowVectorXi>(data+4,5);
cout << "Now v is: " << v << "\n";

输出:

1
2
The mapped vector v is: 1 2 3 4
Now v is: 5 6 7 8 9

用这种方法可以在不知道映射数组在内存中位置的情况下构造一个Map对象:

1
2
3
4
5
6
7
Map<Matrix3f> A(NULL);  // don't try to use this matrix yet!
VectorXf b(n_matrices);
for (int i = 0; i < n_matrices; i++)
{
new (&A) Map<Matrix3f>(get_matrix_pointer(i));
b(i) = A.trace();
}