c++ - 通过 `void *` 和 C++ `extern "C"` 函数调用在 C 中初始化一个 C++ 类

标签 c++ c class opencv pointers

目标是在 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++ - 通过 `void *` 和 C++ `extern "C"` 函数调用在 C 中初始化一个 C++ 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57056307/

相关文章:

c++ - 如何在不递归且不将元素分解为数组的情况下扩展参数包?

c++ - 线程问题 : Control is not coming back to the caller

c - C 中的 SIGSEGV(段错误)

c - 进程退出,返回值 3221226356

php - 从不同的文件和目录自动加载类和函数

c++ - 破坏 _variant_t 导致断点 (C++)

c++ - NormalTexture 在 Wavefront 资源 Material 格式中如何表示?

java - 打包的 C 结构和函数可以移植到 java 吗?

java - 如何使用类 - 蓝牙线程

java - 在 for 循环中找不到符号