通过"void *"和C++"extern "C""函数调用在 C 中初始化 C++ 类
Initialize a C++ class in C through `void *` and C++ `extern "C"` function calls
目标是在C中使用OpenCV 3。 OpenCV有一个C API,但它很久以前就被弃用了。
所以我所做的是一个抽象C++将所有指向class cv::Something
的指针转换为void *
我不能在 C 中取消引用,但可以在完成这项工作的C++ extern "C"
函数之间传递。
为了使用这种抽象,我做了一些C函数,它应该从文件中读取图像并将其保存到新文件中:
#include "foo.h"
#include "libalx/extra/cv.h"
int foo(const char *restrict src, const char *restrict dest)
{
void *img;
int status;
status = -1;
if (alx_cv_alloc_img(&img))
return -1;
if (alx_cv_init_img(img, 1, 1)) // Segfault is here
goto out_free;
if (alx_cv_imread(img, src))
goto out_free;
if (alx_cv_imwrite(img, dest))
goto out_free;
status = 0;
out_free:
alx_cv_free_img(img);
return status;
}
创建此抽象所需的文件如下:
cv.h
:
#pragma once /* libalx/extra/cv.h */
#include <stddef.h>
__attribute__((nonnull))
int alx_cv_alloc_img(void **restrict img);
__attribute__((nonnull))
void alx_cv_free_img (void *restrict img);
__attribute__((nonnull))
int alx_cv_init_img (void *restrict img, ptrdiff_t w, ptrdiff_t h);
__attribute__((nonnull))
int alx_cv_imread (void *restrict img, const char *restrict fname);
__attribute__((nonnull))
void alx_cv_imwrite (const void *restrict img, const char *restrict fname);
cv.hpp
:
#pragma once /* libalx/extra/cv.hpp */
#include <cstddef>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#define restrict __restrict__
extern "C"
{
[[gnu::nonnull]]
int alx_cv_alloc_img(void **restrict img);
[[gnu::nonnull]]
void alx_cv_free_img (void *restrict img);
[[gnu::nonnull]]
int alx_cv_init_img (void *restrict img, ptrdiff_t w, ptrdiff_t h);
[[gnu::nonnull]]
int alx_cv_imread (void *restrict img, const char *restrict fname);
[[gnu::nonnull]]
void alx_cv_imwrite (const void *restrict img, const char *restrict fname);
} /* extern "C" */
namespace alx {
namespace CV {
[[gnu::nonnull]]
int alloc_img (class cv::Mat **restrict img);
[[gnu::nonnull]]
void free_img (class cv::Mat *restrict img);
[[gnu::nonnull]]
int init_img (class cv::Mat *restrict rect,
ptrdiff_t w, ptrdiff_t h);
[[gnu::nonnull]]
int imread (class cv::Mat *restrict img,
const char *restrict fname);
[[gnu::nonnull]]
void imwrite (const class cv::Mat *restrict img,
const char *restrict fname);
} /* namespace CV */
} /* namespace alx */
cv.cpp
:
#include "libalx/extra/cv.hpp"
#include <cstddef>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "libalx/base/stdlib/alloc/mallocs.hpp"
#define restrict __restrict__
int alx::CV::alloc_img (class cv::Mat **restrict img)
{
return alx_mallocs(img, 1);
}
int alx_cv_alloc_img (void **restrict img)
{
return alx::CV::alloc_img((class cv::Mat **)img);
}
void alx::CV::free_img (class cv::Mat *restrict img)
{
img->release();
free(img);
}
void alx_cv_free_img (void *restrict img)
{
alx::CV::free_img((class cv::Mat *)img);
}
int alx::CV::init_img (class cv::Mat *restrict img,
ptrdiff_t w, ptrdiff_t h)
{
if (w < 1 || h < 1)
return 1;
*img = cv::Mat::zeros(cv::Size(w, h), CV_8UC3); // Segfault is here
return 0;
}
int alx_cv_init_img (void *restrict img, ptrdiff_t w, ptrdiff_t h)
{
return alx::CV::init_img((class cv::Mat *)img, w, h);
}
int alx::CV::imread (class cv::Mat *restrict img,
const char *restrict fname)
{
*img = cv::imread(fname, CV_LOAD_IMAGE_COLOR);
if (img->empty())
return -1;
return 0;
}
int alx_cv_imread (void *restrict img, const char *restrict fname)
{
return alx::CV::imread((class cv::Mat *)img, fname);
}
void alx::CV::imwrite (const class cv::Mat *restrict img,
const char *restrict fname)
{
cv::imwrite(fname, *img);
}
void alx_cv_imwrite (const void *restrict img,
const char *restrict fname)
{
alx::CV::imwrite((const class cv::Mat *)img, fname);
}
在这里我使用alx_mallocs
,它是malloc
上的一个外壳:
mallocs.hpp
:
#pragma once /* libalx/base/stdlib/alloc/mallocs.hpp */
#include <cstddef>
/*
* [[gnu::nonnull]]
* int alx_mallocs(type **restrict p, ptrdiff_t nmemb);
*/
#define alx_mallocs(ptr, nmemb) (
{
auto ptr_ = (ptr);
void *vp;
vp = alx_mallocs__(nmemb, sizeof(**ptr_));
*ptr_ = static_cast<typeof(*ptr_)>(vp);
!(*ptr_);
}
)
extern "C"
{
[[gnu::malloc]]
void *alx_mallocs__(ptrdiff_t nmemb, size_t size);
}
mallocs.c
:
#include "libalx/base/stdlib/alloc/mallocs.h"
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
void *alx_mallocs__(ptrdiff_t nmemb, size_t size)
{
if (nmemb < 0)
goto ovf;
if (nmemb > (PTRDIFF_MAX / (ptrdiff_t)size))
goto ovf;
return malloc(size * nmemb);
ovf:
errno = EOVERFLOW;
return NULL;
}
但是,分配和尝试初始化分配的内存似乎还不够。 我的猜测是有一些构造函数没有被调用。
如何做到这一点才能使其不会出现段错误?
而不是尝试用这个调用复制赋值运算符
*img = cv::Mat::zeros(cv::Size(w, h), CV_8UC3);
您可以使用 placement-new 将对象构造到已分配的内存中
new (img) cv::Mat(cv::Size(w, h), CV_8UC3, 0.0);
但是,在将类型构造到以前分配的内存中时,还应确保遵循类型的对齐方式。
相关文章:
- 是否可以初始化不可复制类型的成员变量(或基类)
- C++使用整数的压缩数组初始化对象
- C++初始化基类
- 多成员Constexpr结构初始化
- 复制列表初始化的隐式转换的等级是多少
- 内联映射初始化的动态atexit析构函数崩溃
- 如何在C++中初始化嵌套类中的2个memeber
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 没有用于初始化C++中的变量模板的匹配构造函数
- 在未初始化映射的情况下,将值插入到映射的映射中
- C++成员初始化
- 为什么在C++中首先初始化成员类
- 同时具有"聚合初始化"和"模板推导"
- 初始化具有非默认构造函数的std::数组项的更好方法
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 在C和C++中初始化结构中的数组
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 在函数内部的声明中初始化数组,并在外部使用它
- 继承:构造函数,初始化C++11中基类的类C数组成员