繁体   English   中英

C ++库应该如何允许自定义分配器?

[英]How should C++ libraries allow custom allocators?

在C中,库很容易允许用户通过使用全局函数指针来定制内存分配,该函数指针应该与malloc()和类似于free()的函数表现得类似。 例如,SQLite使用这种方法。

C ++使事情变得复杂,因为分配和初始化通常是融合的。 基本上我们想要获得仅覆盖库的覆盖operator newoperator delete的行为,但实际上没有办法实现(我相当肯定,但不是100%)。

如何在C ++中完成?

这里首先尝试使用函数Lib::make<T>复制new表达式的一些语义。

我不知道这是否有用,但只是为了好玩, 这里是一个更复杂的版本,它也试图复制new[]表达式的语义。

这是一个面向目标的问题,所以我不一定要寻找代码审查。 如果有更好的方法,只需这样说并忽略链接。

(通过“allocator”我只是指分配内存的东西。我不是指STL分配器概念,甚至不是为容器分配内存。)


为什么这可能是可取的:

是一篇来自Mozilla dev的博客文章,他们认为图书馆应该这样做。 他给出了几个允许库用户自定义库分配的C库示例。 我查看了其中一个示例SQLite的源代码,并看到此功能也在内部用于通过故障注入进行测试。 我不是在编写任何需要像SQLite一样防弹的东西,但它似乎仍然是一个明智的想法。 如果不出意外,它允许客户端代码弄清楚“哪个库正在占用我的记忆以及何时?”。

简单回答:不要使用C ++。 对不起,开玩笑。

但是如果你想对C ++中的内存管理,跨库/模块边界以及完全通用的方式采取这种绝对控制,你可能会遇到一些可怕的悲痛。 我建议大多数人寻找不做其他方法的原因。

多年来(实际上几十年),我经历了多次相同基本思想的迭代,尝试将全局级别的运算符new / new [] / delete / delete []天真地重载为基于链接器的特定于平台的解决方案解决方案,我实际上是你现在所希望的点:我有一个系统,可以让我看到每个插件分配的内存量。 但是我没有通过你想要的那种普遍的方式达到这一点(而且我最初也是如此)。

C ++使事情变得复杂,因为分配和初始化通常是融合的。

我会对这个陈述略微扭曲: C ++使事情变得复杂,因为初始化和分配通常是融合的 我所做的就是在这里交换顺序,但最复杂的部分不是分配想要初始化,而是因为初始化经常要分配。

拿这个基本的例子:

struct Foo
{
    std::vector<Bar> stuff;
};

在这种情况下,我们可以通过自定义内存分配器轻松分配Foo:

void* mem = custom_malloc(sizeof(Foo));
Foo* foo = new(foo_mem) Foo;
...
foo->~Foo();
custom_free(foo);

......当然,我们可以将这一切包装成符合RAII,实现异常安全等。

除了现在问题级联。 使用std::vector那个stuff成员会想要使用std::allocator ,现在我们有第二个问题需要解决。 我们可以使用我们自己的分配器来使用std::vector的模板实例化,如果需要传递给分配器的运行时信息,可以覆盖Foo的构造函数,将该信息与分配器一起传递给向量构造函数。

但是Bar呢? 它的构造函数也可能想为各种不同的对象分配内存,因此问题级联,级联和级联。

考虑到这个问题的难度,以及我尝试过的替代的,通用的解决方案以及移植时的悲伤,我已经确定了一种完全去泛化的,有点务实的方法。

我解决的解决方案是有效地重新发明整个C和C ++标准库。 我知道,这很恶心,但在我的情况下,我有更多的借口。 我正在开发的产品实际上是一个引擎和软件开发工具包,旨在允许人们使用任何编译器,C运行时,C ++标准库实现和他们想要的构建设置为其编写插件。 为了允许向量或集合或映射等事物以ABI兼容的方式通过这些中央API传递,除了许多C标准函数之外,还需要滚动我们自己的标准兼容容器。

然后,这个devkit的整个实现围绕这些分配函数:

EP_API void* ep_malloc(int lib_id, int size);
EP_API void ep_free(int lib_id, void* mem);

...并且整个SDK围绕这两个,包括内存池和“子分配器”。

对于我们无法控制的第三方库,我们只是SOL。 其中一些图书馆有同样雄心勃勃的事情,他们想要对他们的记忆管理,并试图覆盖它只会导致各种冲突,并打开各种各样的蠕虫。 当使用像OGL这样想要分配大量系统内存的东西时,也有非常低级别的驱动程序,我们无法做任何事情。

然而,我发现这个解决方案能够很好地回答基本问题:“谁/什么在占据所有这些记忆?” 很快:一个问题通常比与时钟周期相关的类似问题(我们可以启动任何分析器)更难回答。 它仅适用于我们控制下的代码,使用此SDK,但我们可以在每个模块的基础上使用此系统获得非常彻底的内存故障。 我们还可以设置内存使用的表面上限,以确保实际上正确处理内存不足错误,而不会实际耗尽系统中可用的所有连续页面。

所以在我的情况下,这个问题是通过政策解决的:通过构建统一的编码标准和符合它的中央库,在整个代码库中使用(以及第三方为我们的系统编写插件)。 这可能不是您正在寻找的答案,但这最终成为我们发现的最实用的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM