【问题标题】:Using eigen objects in C在 C 中使用特征对象
【发布时间】:2022-01-05 07:18:01
【问题描述】:

我想将 Eigen 的部分功能封装在 C 中,但我很好奇在这种情况下自动存储持续时间将如何工作。例如:

/* eigenwrapper.h */
#ifdef __cplusplus
extern "C" {
#endif


void* create_matrix(int r, int c);
//and other declarations like addition, multiplication, delete ... ....

#ifdef __cplusplus
}
#endif

`

/* eigenwrapper.cxx */
#include <eigen headers>
#include "eigenwrapper.h"

extern "C" {

void* create_matrix(int r, int c) {
    return &MatrixXf(r,c);

}

// and other definitions

}

`

/*main.c*/
#include "eigenwrapper.h"

int main(void) {
    m = create_matrix(3,3);
    /* working with m */
}

我假设 Eigen 或 C++ 会跟踪对象的引用数量,但是当我将对象的指针返回给 C 函数时,这会起作用吗?离开create_matrix函数时对象会被解构吗?

如果自动存储时长不能这样工作,我应该使用new 关键字进行动态分配吗?例如return new MatrixXf(r,c); 那么当我有一个函数返回matA * matB 时,我怎样才能获得动态分配的newed 矩阵呢?

【问题讨论】:

  • 如果你想在 C 中管理自动存储,你必须明确标记什么时候进入和超出范围。一个突出的例子是 Python C API。如果您正在编写适用于 Python 数据类型的 C 代码,您必须在变量“进入范围”时调用 Py_INCREF,在变量“退出范围”时调用 Py_DECREF。这是你的工作,因为 C 不会为你做这件事。
  • &amp;MatrixXf(r,c) 没有意义。使用new 创建一个对象并获取指向它的指针。返回到 C 代码后,将指针用作 opaque pointer 并将其传递给所有需要它的 C++ 函数。然后,C++ 函数必须执行 reinterpret_cast&lt;MatrixXf*&gt;(ptr) 才能获得指向该对象的正确指针。您还需要一个显式的 API 函数来删除该对象。
  • 另一方面,在 C++ 中,所有指向对象的指针都可以隐式转换为 void*,您不需要为此进行强制转换。在这一点上,每当你觉得需要在 C++ 中进行 C 风格的转换时,你应该把它当作你可能做错了什么的迹象。
  • 在 C 中包装矩阵 c++ 库没有意义。如果你真的需要纯 c,找一个带有 c API 的矩阵库,比如 LAPACK。但请考虑在 C++ 中进行矩阵计算。
  • “我假设 Eigen 或 C++ 会跟踪对象的引用数量”——不,这不会发生,除非你使用像 std::shared_ptr 这样的东西。我认为,获取右值的地址也是不合法的(我不是语言律师......)

标签: c++ c eigen


【解决方案1】:

正如其他人所指出的,C++ 不跟踪引用。如果你想要一个 C API,你必须自己处理。

不透明的指针

至于将 Eigen 函数包装到 C API 中,我会使用不透明指针而不是 void*,这样您至少有一些类型安全性。

这是一个合适的标题:

#pragma once

#include <stddef.h>
/* using ptrdiff_t */

/** forward-declared opaque handle for matrix */
typedef struct EigenMatrixXf EigenMatrixXf;

#ifdef __cplusplus
extern "C" {
#endif

EigenMatrixXf* eigen_matrixxf_create(ptrdiff_t rows, ptrdiff_t cols);
EigenMatrixXf* eigen_matrixxf_add(
      const EigenMatrixXf* left, const EigenMatrixXf* right);
void eigen_matrixxf_free(EigenMatrixXf* self);

#ifdef __cplusplus
} /* extern "C" */
#endif

这里是cpp文件


struct EigenMatrixXf
{
    Eigen::MatrixXf mat;
};
EigenMatrixXf* eigen_matrixxcf_create(ptrdiff_t rows, ptrdiff_t cols)
try {
    return new EigenMatrixXf{ Eigen::MatrixXf(rows, cols) };
} catch(std::bad_alloc&) {
    errno = ENOMEM;
    return nullptr;
}
EigenMatrixXf* eigen_matrixxf_add(
      const EigenMatrixXf* left, const EigenMatrixXf* right)
try {
    return new EigenMatrixXf{ left->mat + right->mat };
} catch(std::bad_alloc&) {
    errno = ENOMEM;
    return nullptr;
}
void eigen_matrixxcf_free(EigenMatrixXf* self)
{ delete self; }

如果您想使用固定大小的 Eigen 对象,请在此处阅读分配器问题:https://eigen.tuxfamily.org/dox/group__TopicStructHavingEigenMembers.html

映射 C 结构

您还可以使用 Eigen 的 Map 函数将 Eigen view 赋予 C 结构。 https://eigen.tuxfamily.org/dox/group__TutorialMapClass.html 在 API 设计和性能方面有一些权衡(例如动态分配的数量、对齐等)

标题:

#pragma once

#include <stddef.h>
/* using ptrdiff_t */

typedef struct EigenMatrixXf
{
    float* ptr;
    ptrdiff_t rows, cols;
} EigenMatrixXf;

#ifdef __cplusplus
extern "C" {
#endif

EigenMatrixXf eigen_matrixxf_create(ptrdiff_t rows, ptrdiff_t cols);
EigenMatrixXf eigen_matrixxf_add(
    const EigenMatrixXf* left, const EigenMatrixXf* right);
void eigen_matrixxf_free(EigenMatrixXf* self);

#ifdef __cplusplus
} /* extern "C" */
#endif

Cpp 文件:


inline Eigen::MatrixXf::MapType map(EigenMatrixXf& ctype)
{ return Eigen::MatrixXf::MapType(ctype.ptr, ctype.rows, ctype.cols); };

inline Eigen::MatrixXf::ConstMapType map(const EigenMatrixXf& ctype)
{ return Eigen::MatrixXf::ConstMapType(ctype.ptr, ctype.rows, ctype.cols); };

EigenMatrixXf eigen_matrixxf_create(ptrdiff_t rows, ptrdiff_t cols)
try {
    return EigenMatrixXf { new float[rows * cols], rows, cols };
} catch(std::bad_alloc&) {
    errno = ENOMEM;
    return EigenMatrixXf { nullptr, 0, 0 };
}
EigenMatrixXf eigen_matrixxf_add(
      const EigenMatrixXf* left, const EigenMatrixXf* right)
{
    EigenMatrixXf rtrn = eigen_matrixxf_create(left->rows, left->cols);
    if(! rtrn.ptr)
      return rtrn;
    map(rtrn) = map(*left) + map(*right);
    return rtrn;
}
void eigen_matrixxf_free(EigenMatrixXf* self)
{ delete[] self->ptr; }

【讨论】:

  • 谢谢!映射方法很优雅。我只是想知道将float* 映射到MatrixXf 的成本是多少。
  • @GabriellaChaos 几乎没有成本。编译器将优化对象。如果您确保您的数组是 16 字节对齐的,那么您也可以使用 MapAligned 而不是 Map。从技术上讲,这将为旧 CPU 编写更好的代码,但它还将包括运行时检查以查看指针是否真的对齐。所以恕我直言,这是不值得的。
猜你喜欢
  • 1970-01-01
  • 2016-10-05
  • 1970-01-01
  • 2015-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-14
相关资源
最近更新 更多