我有一个很好的库来管理需要返回特定字符串列表的文件.因为我将要使用它的唯一代码是C++(和Java,但是通过JNI使用C++)我决定使用标准库中的vector.库函数看起来有点像这样(其中FILE_MANAGER_EXPORT是平台定义的导出要求):
extern "C" FILE_MANAGER_EXPORT void get_all_files(vector<string> &files)
{
files.clear();
for (vector<file_struct>::iterator i = file_structs.begin(); i != file_structs.end(); ++i)
{
files.push_back(i->full_path);
}
}
Run Code Online (Sandbox Code Playgroud)
我使用向量作为参考而不是返回值的原因是试图保持内存分配的合理性,因为我真的不高兴我在c ++返回类型周围有外部"C"(谁知道为什么,我的理解是所有extern" C"确实可以防止编译器中的名称损坏.无论如何,与其他c ++一起使用的代码通常如下:
#if defined _WIN32
#include <Windows.h>
#define GET_METHOD GetProcAddress
#define OPEN_LIBRARY(X) LoadLibrary((LPCSTR)X)
#define LIBRARY_POINTER_TYPE HMODULE
#define CLOSE_LIBRARY FreeLibrary
#else
#include <dlfcn.h>
#define GET_METHOD dlsym
#define OPEN_LIBRARY(X) dlopen(X, RTLD_NOW)
#define LIBRARY_POINTER_TYPE void*
#define CLOSE_LIBRARY dlclose
#endif
typedef void (*GetAllFilesType)(vector<string> &files);
int main(int argc, char **argv)
{
LIBRARY_POINTER_TYPE manager = LOAD_LIBRARY("library.dll"); //Just an example, actual name is …Run Code Online (Sandbox Code Playgroud) 我有两个dll导出的类A和B.一个声明包含一个在其签名中使用std :: vector的函数,如:
class EXPORT A{
// ...
std::vector<B> myFunction(std::vector<B> const &input);
};
Run Code Online (Sandbox Code Playgroud)
(EXPORT是相应的通用宏_dllspec(dllexport)/ _declspec(dllimport).)
阅读有关在DLL接口中使用STL类的相关问题,我总结一下:
在DLL接口中使用std :: vector将要求使用相同编译器的相同版本编译该DLL的所有客户端,因为STL容器不是二进制兼容的.更糟糕的是,取决于客户端与其他DLL一起使用该DLL,''不稳定''DLL API可以在安装系统更新时破坏这些客户端应用程序(例如Microsoft KB软件包)(真的吗?).
尽管如此,如果需要,std :: vector可以通过导出来在DLL API中使用std::vector<B>:
template class EXPORT std::allocator<B>;
template class EXPORT std::vector<B>;
Run Code Online (Sandbox Code Playgroud)
但是,当人们想要使用std :: vector作为A(http://support.microsoft.com/kb/168958)的成员时,通常会在上下文中提到这一点.
以下Microsoft支持文章讨论了如何通过可执行文件中的指针或引用访问DLL中创建的std :: vector对象(http://support.microsoft.com/default.aspx?scid=kb;EN-US; Q172396).上述使用的解决方案template class EXPORT ...似乎也适用.然而,在第一个要点下总结的缺点似乎仍然存在.
要完全摆脱这个问题,需要包装std :: vector并更改myFunctionPIMPL等的签名.
我的问题是:
以上摘要是正确的,还是我在这里想念一些必要的东西?
为什么我的类'A'的编译没有生成警告C4251(类'std :: vector <_Ty>'需要让dll-interface被...的客户端使用)?我没有关闭编译器警告,并且myFunction在导出的A类(使用VS2005)中使用std :: vector in时没有任何警告.
myFunction在A中正确导出需要做什么?是否可以直接导出std::vector<B>和B的分配器?
返回std :: vector by-value有什么含义?假设客户端可执行文件已使用不同的编译器(-version)编译.在复制向量的情况下返回by-value时是否仍然存在问题?我猜是.类似地,将std :: vector作为常量引用传递:可以访问std::vector<B>(可能由使用不同编译器(-version)编译的可执行文件构造)导致内部麻烦 …
对于在许多工具链API/ABI兼容性与相同的二进制,这是很好 众所周知 的是 STL容器,std::string和其他标准库类,如输入输出流被禁止的公共报头.(例外情况是,如果为每个受支持的工具链版本分配一个构建;一个为最终用户编译提供没有二进制文件的源,在本例中不是首选选项;或者一个转换为其他一些内联容器,以便不同的std实现不会被库提取.)
如果已经有一个已发布的库API没有遵循此规则(请求朋友),那么最好的路径是什么,同时保持尽可能多的向后兼容性,并且我不能支持编译时断点?我需要支持Windows和Linux.
重新考虑我正在寻找的ABI兼容性水平:我不需要它是疯狂的未来证明.我主要是为每个版本的多个流行的Linux发行版只做一个库二进制文件.(目前,我为每个编译器发布一个,有时为特殊发行版(RHEL vs Debian)发布特殊版本.与MSVC版本相同的问题 - 所有支持的MSVC版本的一个DLL将是理想的.)其次,如果我不'在破解修复版本中打破API,我希望它与ABI兼容,并且无需重建客户端应用程序即可替换掉DLL/SO.
我有三个案例,有一些初步建议,模仿Qt到一定程度.
旧的公共API:
// Case 1: Non-virtual functions with containers
void Foo( const char* );
void Foo( const std::string& );
// Case 2: Virtual functions
class Bar
{
public:
virtual ~Bar() = default;
virtual void VirtFn( const std::string& );
};
// Case 3: Serialization
std::ostream& operator << ( std::ostream& os, const Bar& bar );
Run Code Online (Sandbox Code Playgroud)
从理论上讲,我们可以将std::string使用转换为非常类似于std::string_view我们库的API/ABI控件的类.它将在我们的库头中进行转换,std::string以便编译的库仍然接受但是独立于std::string实现并且向后兼容:
新API:
class …Run Code Online (Sandbox Code Playgroud)