如何为delete编写一个C包装器,既快速又释放给定的任何类型,而不告诉它是什么类型

How to write a C wrapper for delete that would be fast yet free any type given to it with out telling it what type

本文关键字:类型 释放 任何 它是什么 delete 包装 一个      更新时间:2023-10-16

有一个完整的OpenCV c++函数的C包装器列表,如下所示。它们都返回一个"new"。我不能改变它们,因为它们正在成为OpenCV的一部分,它会使我的库完美地有一个持续更新的框架来包裹。

Mat* cv_create_Mat() {
    return new Mat();
}

我不能为c++函数重写C包装器,所以我写了一个像下面这样的删除包装器,我试图释放的内存是一个Mat*, Mat是一个OpenCV c++类…下面的删除包装器可以工作。绝对没有内存泄漏。

我有很多其他的C包装器OpenCV c++函数,虽然,返回一个新的指针…至少有10或15个,我的意图是不必为所有这些都编写单独的删除包装器。如果你能告诉我如何写一个删除包装,将释放任何指针后,它不必被告知哪一种类型的释放和快速,太棒了。

这些都是我的意图,我知道你们这些伟大的程序员可以帮助我解决这个问题:)…简而言之……我有cvsvmpparams *, Brisk*, RotatedRect*, CVANN_MLP*指针还有一些其他的,都需要与一个包装器释放…一个去包装c++的delete将释放任何东西…在这方面任何帮助都是非常宝贵的。
void delete_ptr(void* ptr) {
    delete (Mat*)ptr;
}
编辑:我需要你们两个人中的一个告诉我如何运行你们发布的代码…当我把Emacs g++放在main上面并使用Free(cv_create_Mat)运行时,注册表版本不起作用;一个新的Mat*创建者和存根以相同的方式运行得到5个错误消息。我需要精确的编译指令。我的目的是能够把这个编译成。so文件,你真的很关注这篇文章,虽然,我很感激,谢谢你

这样如何,然后让编译器处理所有的专门化:

template <typename T>
void delete_ptr(T *ptr) {
    delete ptr;
}

delete操作符不仅释放内存,还调用析构函数,并且必须在类型化指针(而不是void*)上调用,以便它知道要调用哪个类的析构函数。您需要为每种类型使用单独的包装器。

对于没有析构函数的POD类型,您可以分配malloc()而不是new,以便调用者可以使用free()

我建议不要使用通用的delete_ptr函数。

由于创建和删除是成对的,我将创建一个用于特定类型的创建和删除。

Mat* cv_create_Mat();
void cv_delete_Mat(Mat*);

如果你这样做,你正在处理的对象类型就会少一些歧义。此外,cv_delete_Mat(Mat*)的实现将更不容易出错,并且必须承担更少的责任。

void cv_delete_Mat(Mat* m)
{
  delete m;
}

这样的泛型操作只能通过删除类型信息(void*)或单独确保所有包装器函数存在来实现。

C的ABI不允许函数重载,c++的delete关键字正是你所要求的那种通用包装器。

也就是说,你可以做一些事情,但没有一件比你已经提出的建议更简单。您编写的任何泛型c++代码都将无法从C中调用。

你可以给你的对象添加知道如何销毁自己的成员,例如

类CVersionOfFoo:公共Foo {…静态void deleter(CVersionOfFoo* p){删除p;}

};

但是c无法访问。

最后一个选项是设置某种形式的手动注册表,其中对象将其指针与删除函数一起注册。但是,这将是更多的工作,更难调试比仅仅编写包装。

—EDIT—

注册表例子;如果你有c++ 11:

#include <functional>
#include <map>
/* not thread safe */
typedef std::map<void*, std::function<void(void*)>> ObjectMap;
static ObjectMap s_objectMap;
struct Foo {
    int i;
};
struct Bar {
    char x[30];
};
template<typename T>
T* AllocateAndRegister() {
    T* t = new T();
    s_objectMap[t] = [](void* ptr) { delete reinterpret_cast<T*>(ptr); };
    return t;
}
Foo* AllocateFoo() {
    return AllocateAndRegister<Foo>();
}
Bar* AllocateBar() {
    return AllocateAndRegister<Bar>();
}
void Free(void* ptr) {
    auto it = s_objectMap.find(ptr);
    if (it != s_objectMap.end()) {
        it->second(ptr); // call the lambda
        s_objectMap.erase(it);
    }
}

如果你没有c++ 11…您必须创建一个delete函数。

就像我说的,它比你创建的包装器要多。

这并不是c++不能做到这一点的情况——c++完全有能力做到这一点,但是你试图在C中做到这一点而C没有提供自动做到这一点的工具。

核心问题是c++中的delete需要一个类型,通过C接口传递指针会丢失该类型。问题是如何以一种通用的方式恢复这种类型。以下是一些选择。

请记住delete做了两件事:调用析构函数和释放内存。

  1. 每种类型单独的功能。最后一招,你想要避免的。
  2. 对于具有普通析构函数的类型,您可以将void指针强制转换为您喜欢的任何类型,因为它所做的只是释放内存。这样就减少了函数的数量。[这是未定义的行为,但它应该工作]
  3. 使用运行时类型信息恢复指针的type_info,然后将其动态强制转换为正确的类型以删除。
  4. 修改你的create函数,将指针存储在一个带有type_info的字典中。在删除时,检索类型并使用动态强制转换来删除指针。

尽管如此,我可能会使用选项1,除非有数百种东西。您可以编写带有显式实例化的c++模板来减少所需的代码量,或者编写带有标记粘贴的宏来生成唯一名称。以下是一个示例(编辑过):

#define stub(T) T* cv_create_ ## T() { return new T(); } 
              void cv_delete_ ## T(void *p) { delete (T*)p; }
stub(Mat);
stub(Brisk);

字典方法的一个优点是用于调试。您可以在运行时跟踪new和delete,并确保它们正确匹配。如果调试非常重要,我会选择这个选项,但它需要更多的代码来完成。