I want to wrap part of Eigen's feature in C, but I am curious how would the automatic storage duration works in such case. For example:
/* 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 */
}
I assume that Eigen or C keep tracks of the number of references of the object, but will that work when I return pointer of object to C functions? Will the object being deconstructed when leaving the create_matrix
function?
If the automatic storage duration won't work this way, Should I use the new
keyword for dynamic allocation? e.g. return new MatrixXf(r,c);
Then how could I obtain dynamically allocated, new
ed, matrices when I have a function returns matA * matB
?
CodePudding user response:
As others have noted, C does not keep track of references. If you want a C API, you have to deal with this yourself.
Opaque pointers
As far as wrapping Eigen functions into C API, I would go with opaque pointers instead of void* so that you have at least some type safety.
Here is a suitable header:
#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
And here the cpp file
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; }
If you want to use fixed-size Eigen objects, read up on the allocator issues here: https://eigen.tuxfamily.org/dox/group__TopicStructHavingEigenMembers.html
Mapping C structs
You can also use Eigen's Map
function give an Eigen view to a C struct. https://eigen.tuxfamily.org/dox/group__TutorialMapClass.html
There are a few tradeoffs here when it comes to API design and performance (e.g. number of dynamic allocations, alignment, etc.)
Header:
#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 file:
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; }