针对 GNU++98 / libstdc++ link 编译的应用程序是否可以针对 C++11 / libc++ 构建的 dylib?
Can an app compiled against GNU++98 / libstdc++ link to a dylib built against C++11 / libc++?
我有一个使用 Xcode 7.2.1 构建的 C++ 应用程序,使用 GNU++98 方言编译并链接到 libstdc++。我需要调用一个用 C++11 编译并链接到 libc++ 的 dylib。
在 dylib 的 class 成员中使用默认属性可见性:
extern __attribute__((visibility("default"))) void foo(std::string const&, int);
...表明不存在 ABI 兼容性(至少对于字符串而言)——任何参数为 std::string 的 dylib 函数都无法链接到应用程序。
是创建重载 C++ 成员函数的适当解决方案,该成员函数仅将在 dylib 端构建的 char* 参数传递给 std::string,或者是否存在其他问题需要创建纯 C API 为 dylib?
注意:现在已经阅读了 This excellent explanation 关于 ABI 兼容性问题,看起来确实没有什么希望,只能为 std::string 参数构建一个基于字符的 API...
使用标准布局 类 编写您的 API。
标准布局 类 可以很花哨。
这是我脑海中未编译的 array_view
:
template<class Container>
using data_ptr_type = decltype( std::declval<Container>().data() );
template<bool b>
using bool_kt = std::integral_constant<bool, b>;
template<class T>
struct array_view {
T* b = nullptr;
T* e = nullptr;
T* begin() const { return b; }
T* end() const { return b; }
template<class U>
using ptr_is_compatible = bool_kt<
std::is_same< U, T* >{} || std::is_same< U, std::remove_const_t<T>* >{} ||
std::is_same< U, std::remove_volatile_t<T>* >{} || std::is_same< U, std::remove_cv_t<T>* >{}
>;
// convert from .data() and .size() containers:
template<class In,
std::enable_if_t<
ptr_is_compatible< data_ptr_type< In& > >{}, int
> = 0
>
array_view( In&& in ):array_view(in.data(), in.size()) {}
// special ones:
array_view()=default;
array_view(array_view const&)=default;
array_view& operator=(array_view const&)=default;
// manual ones:
array_view( T* ptr, std::size_t N ):array_view(ptr, ptr+N) {}
array_view( T* s, T* f ):b(s), e(f) {}
// from C-style array:
template<class U, std::size_t N,
std::enable_if_t<
ptr_is_compatible< U* >{}
,int> = 0
>
array_view( U(&arr)[N] ):array_view(arr, N) {}
template<class Container>
Container copy_to() const {
return {begin(), end()};
}
};
虽然花哨,但它是标准布局。所以只有最疯狂的 ABI 更改才会破坏它。
现在您的头文件显示为:
extern __attribute__((visibility("default"))) void foo(array_view<const char>, int);
并且调用者可以用 foo( "hello", 7 )
或 foo( std::string("hello"), 42 )
或 foo( std::vector<char>{'a', 'b', 'c'}, 18 )
或其他任何方式调用它。你不在乎。
指向缓冲区起始端的指针是在调用方完成的,因此您不知道传递的内容的布局。
在内部,您可以根据需要编组回带有 arg.to<std::string>()
的容器。
我有一个使用 Xcode 7.2.1 构建的 C++ 应用程序,使用 GNU++98 方言编译并链接到 libstdc++。我需要调用一个用 C++11 编译并链接到 libc++ 的 dylib。
在 dylib 的 class 成员中使用默认属性可见性:
extern __attribute__((visibility("default"))) void foo(std::string const&, int);
...表明不存在 ABI 兼容性(至少对于字符串而言)——任何参数为 std::string 的 dylib 函数都无法链接到应用程序。
是创建重载 C++ 成员函数的适当解决方案,该成员函数仅将在 dylib 端构建的 char* 参数传递给 std::string,或者是否存在其他问题需要创建纯 C API 为 dylib?
注意:现在已经阅读了 This excellent explanation 关于 ABI 兼容性问题,看起来确实没有什么希望,只能为 std::string 参数构建一个基于字符的 API...
使用标准布局 类 编写您的 API。
标准布局 类 可以很花哨。
这是我脑海中未编译的 array_view
:
template<class Container>
using data_ptr_type = decltype( std::declval<Container>().data() );
template<bool b>
using bool_kt = std::integral_constant<bool, b>;
template<class T>
struct array_view {
T* b = nullptr;
T* e = nullptr;
T* begin() const { return b; }
T* end() const { return b; }
template<class U>
using ptr_is_compatible = bool_kt<
std::is_same< U, T* >{} || std::is_same< U, std::remove_const_t<T>* >{} ||
std::is_same< U, std::remove_volatile_t<T>* >{} || std::is_same< U, std::remove_cv_t<T>* >{}
>;
// convert from .data() and .size() containers:
template<class In,
std::enable_if_t<
ptr_is_compatible< data_ptr_type< In& > >{}, int
> = 0
>
array_view( In&& in ):array_view(in.data(), in.size()) {}
// special ones:
array_view()=default;
array_view(array_view const&)=default;
array_view& operator=(array_view const&)=default;
// manual ones:
array_view( T* ptr, std::size_t N ):array_view(ptr, ptr+N) {}
array_view( T* s, T* f ):b(s), e(f) {}
// from C-style array:
template<class U, std::size_t N,
std::enable_if_t<
ptr_is_compatible< U* >{}
,int> = 0
>
array_view( U(&arr)[N] ):array_view(arr, N) {}
template<class Container>
Container copy_to() const {
return {begin(), end()};
}
};
虽然花哨,但它是标准布局。所以只有最疯狂的 ABI 更改才会破坏它。
现在您的头文件显示为:
extern __attribute__((visibility("default"))) void foo(array_view<const char>, int);
并且调用者可以用 foo( "hello", 7 )
或 foo( std::string("hello"), 42 )
或 foo( std::vector<char>{'a', 'b', 'c'}, 18 )
或其他任何方式调用它。你不在乎。
指向缓冲区起始端的指针是在调用方完成的,因此您不知道传递的内容的布局。
在内部,您可以根据需要编组回带有 arg.to<std::string>()
的容器。