在设计C ++库时,我认为在公共界面中包含std::vector
等标准库容器是不好的做法(参见例如Implications of using std::vector in a dll exported function)。
如果我想公开一个获取或返回对象列表的函数,该怎么办?我可以使用一个简单的数组,但是我必须添加一个count
参数,这会使界面更麻烦,更不安全。例如,如果我想使用map
,它也无济于事。我想像Qt这样的库定义了自己的容器,这些容器可以安全地导出,但我宁愿不将Qt作为依赖项添加,而且我不想滚动自己的容器。
在库界面中处理容器的最佳做法是什么?是否可能有一个小容器实现(最好只有一两个文件,我可以使用许可许可证),我可以用作“粘合剂”?或者甚至有一种方法可以使std::vector
等在.DLL / .so边界和不同编译器之间安全吗?
答案 0 :(得分:4)
实际上,这不仅适用于STL容器,而且几乎适用于任何C ++类型(特别是所有其他标准库类型)。
由于ABI未标准化,您可能遇到各种麻烦。通常,您必须为每个受支持的编译器版本提供单独的二进制文件,以使其工作。获得真正可移植DLL的唯一方法是坚持使用普通的C接口。这通常会导致类似COM的内容,因为您必须确保所有分配和匹配的解除分配都发生在同一个模块中,并且不会向用户公开实际对象布局的详细信息。
答案 1 :(得分:3)
您可以实现模板功能。这有两个好处:
例如,将其放在头文件中:
template <typename Iterator>
void foo(Iterator begin, Iterator end)
{
for (Iterator it = begin; it != end; ++it)
bar(*it); // a function in your library, whose ABI doesn't depend on any container
}
然后,您的用户可以使用任何容器类型调用foo,甚至是他们发明的您不知道的容器类型。
一个缺点是你需要暴露实现代码,至少对foo来说。
编辑:您还说过您可能想要返回一个容器。考虑像回调函数这样的替代方法,就像C:
中的黄金时代一样typedef bool(*Callback)(int value, void* userData);
void getElements(Callback cb, void* userData) // implementation in .cpp file, not header
{
for (int value : internalContainer)
if (!cb(value, userData))
break;
}
这是一个相当古老的学校“C”方式,但它为您提供了一个稳定的界面,基本上任何调用者都可以使用(即使是实际的C代码也有微小的变化)。两个怪癖是void * userData让用户在那里堵塞一些上下文(比如他们想要调用成员函数)和bool返回类型让回调告诉你停止。您可以使用std :: function或其他任何方式使回调变得更加漂亮,但这可能会破坏您的其他一些目标。
答案 2 :(得分:3)
TL; DR 如果为各种受支持的(ABI +标准库实现)集合分发源代码或已编译的二进制文件,则没有问题。
一般来说,后者被视为繁琐(有原因),因此是指南。
我信任挥手指导,尽我可以扔掉它们......我鼓励你这样做。
本指南源于ABI兼容性问题:ABI是一组复杂的规范,用于定义已编译库的精确接口。它特别包括:
有关详情,请查看Itanium ABI。与具有非常简单的ABI的C相反,C ++具有更复杂的表面区域......因此为它创建了许多不同的ABI。
除了ABI兼容性之外,标准库实施也存在问题。大多数编译器都有自己的标准库实现,并且这些实现彼此不兼容(例如,它们不会以相同的方式表示std::vector
,即使所有实现都实现相同的接口和保证)。
因此,如果编译的二进制文件(可执行文件或库)都是针对相同的ABI和标准库实现的兼容版本编译的,则它们只能与另一个编译的二进制文件混合并匹配。
干杯:如果您分发源代码并让客户端编译,则没有问题。
答案 3 :(得分:0)
如果您使用的是C ++ 11,则可以使用cppcomponents。 https://github.com/jbandela/cppcomponents
这将允许您使用std :: vector作为参数或返回使用不同编译器或标准库创建的Dll /或.so文件的值。请查看我对类似问题的回答,例如Passing reference to STL vector over dll boundary
请注意,您需要在CPPCOMPONENTS_REGISTER(ImplementFiles)
语句后添加CPPCOMPONENTS_DEFINE_FACTORY()