C++ 混合C++;同一项目中的风味
在同一个项目中混合使用C++98和C++11安全吗?我所说的“混合”不仅指链接对象文件,还指使用C++98和C++11编译的源代码中包含的公共头文件 这个问题的背景是希望将大型代码库的至少一部分转换为C++11。代码的一部分是C++ C++语言,编译为在GPU或CPU上执行,相应的编译器此时不支持C++ 11。然而,大部分代码只用于CPU,并且可以用C++的味道编译。一些头文件包含在CPU+GPU和仅CPU源文件中 如果我们现在用C++11编译器编译只使用CPU的源文件,我们能确信不受不良副作用的影响吗?在实践中,也许是这样 C++11和C++03的标准库在C++ 混合C++;同一项目中的风味,c++,c++11,cuda,compilation,compatibility,C++,C++11,Cuda,Compilation,Compatibility,在同一个项目中混合使用C++98和C++11安全吗?我所说的“混合”不仅指链接对象文件,还指使用C++98和C++11编译的源代码中包含的公共头文件 这个问题的背景是希望将大型代码库的至少一部分转换为C++11。代码的一部分是C++ C++语言,编译为在GPU或CPU上执行,相应的编译器此时不支持C++ 11。然而,大部分代码只用于CPU,并且可以用C++的味道编译。一些头文件包含在CPU+GPU和仅CPU源文件中 如果我们现在用C++11编译器编译只使用CPU的源文件,我们能确信不受不良副作用
std
namespace对象的布局上存在分歧是比较常见的。例如,sizeof(std::vector)
在MSVC领域的各种编译器版本中发生了显著的变化。(随着他们的优化,它变得更小)
其他示例可能是编译器围栏两侧的不同堆
因此,您必须小心地在两个源代码树之间设置“防火墙”
现在,一些编译器试图最小化这种二进制兼容性更改,即使是以违反标准为代价。我相信没有大小计数器的std::list
就是一个例子(这违反了C++11,但我记得至少有一家供应商提供了不符合标准的std::list
,以保持二进制兼容性——我不记得是哪家了)
对于这两个编译器(C++03和C++11中的一个编译器是不同的编译器),您将获得一些ABI保证。ABI可能会同意该语言的大部分内容,在这一点上,您是相对安全的
为了安全合理,您将希望将其他编译器版本文件视为第三方DLL(延迟加载库),它们不链接到同一C++标准库。这意味着从一个传递到另一个的任何资源都必须使用销毁代码打包(即,返回到要销毁的DLL)。您要么调查两个标准库的ABI,要么避免在公共头文件中使用它,这样您就可以在DLL之间传递智能指针之类的东西
一种更安全的方法是使用其他代码库将自己剥离到一个C风格的接口,并且只在两个代码库之间传递句柄(不透明类型)。为了使这个问题变得明智,只需在一些漂亮的C++代码中只使用MJO来包装C风格的界面,就不要在代码库之间传递这些C++对象。 所有这些都是一种痛苦 例如,假设您有一个std::string get_some_string(HANDLE)
函数,并且您不信任ABI稳定性
所以你有3层
namespace internal {
// NOT exported from DLL
std::string get_some_string(HANDLE) { /* implementation in DLL */ }
}
namespace marshal {
// exported from DLL
// visible in external headers, not intended to be called directly
void get_some_string(HANDLE h, void* pdata, void(*callback)( void*, char const* data, std::size_t length ) ) {
// implementation in DLL
auto r = ::internal::get_some_string(h);
callback( pdata, r.data(), r.size() );
}
}
namespace interface {
// exists in only public header file, not within DLL
inline std::string get_some_string(HANDLE h) {
std::string r;
::marshal::get_some_string(h, &r,
[](void* pr, const char* str, std::size_t length){
std::string& r = *static_cast<std::string*>(pr);
r.append( str, length );
}
);
return r;
}
}
名称空间内部{
//未从DLL导出
string获取一些字符串(句柄){/*在DLL中实现*/}
}
名称空间封送{
//从DLL导出
//在外部标头中可见,不打算直接调用
void获取一些字符串(句柄h,void*pdata,void(*回调)(void*,char const*data,std::size\t length)){
//DLL中的实现
自动r=::内部::获取一些字符串(h);
回调(pdata,r.data(),r.size());
}
}
名称空间接口{
//仅存在于公共头文件中,而不存在于DLL中
内联std::string获取一些字符串(句柄h){
std::字符串r;
::封送:获取一些字符串(h,&r,
[](void*pr,const char*str,std::size\u t length){
std::string&r=*静态(pr);
r、 追加(str,长度);
}
);
返回r;
}
}
因此,DLL之外的代码执行一个auto s=::interface::get_some_string(handle)代码>,它看起来像一个C++接口。
DLL中的代码实现std::string::internal::get_some_string(句柄)代码>
marshal
的get_some_string
在两者之间提供了一个C风格的接口,它提供了比依赖std::string
的布局和实现更好的二进制兼容性,从而在DLL和使用DLL的代码之间保持稳定
接口
的std::string
完全存在于非DLL代码中。internal
std::string
完全存在于DLL代码中。封送处理代码将数据从一端移动到另一端。我曾经问过一个问题。关于std::list
:那将是libstdc++(随gcc一起提供)。这个答案需要一个ODR的示例,它隐藏在一个编译器后面,手里拿着一个标有“未定义行为”的bat,等待着跳出来打败程序员。