共享库包装器的ODR与C++版本兼容性问题问询
ODR违规疑问:C17编译的API提供者与C11客户端的模板函数定义差异
为简化说明,本文省略了诸如将void*替换为不透明struct的typedef、Windows调用约定、固定整数类型等细节。
假设存在以下文件:
CApi.h——具备C链接的共享库头文件
用于提升可移植性并隐藏专有代码:
#define LIB_API // library import/export details extern "C" { typedef int error; error LIB_API lib_foo_create(void ** foo); error LIB_API lib_foo_destroy(void * foo); error LIB_API lib_foo_func(void * foo, int * out); error LIB_API lib_bar_create(void ** bar); error LIB_API lib_bar_destroy(void * bar); error LIB_API lib_bar_func(void * bar, int * out); // 内部使用CxxApi.hpp中的Foo::func,基于C++17实现 } // extern C
CxxApi.hpp——仅头文件的包装器
用于简化API使用:
#include "CApi.h" namespace lib { namespace detail { template < typename Return = void, typename Func, typename... Args > Return c_api(Func func) { // 不确定客户端与提供者用不同版本编译时对ODR的影响,但双方看不到彼此的代码使用情况 #if __cplusplus >= 201703L // C++17 // 更高效的实现(比如折叠表达式) #else // 回退实现(比如递归) #endif } } // namespace lib::detail class Foo { public: Foo() { detail::c_api(lib_foo_create, &handle_); } ~Foo() { detail::c_api(lib_foo_destroy, handle_); } int func() { return detail::c_api<int>(lib_foo_func, handle_); } private: void * handle_; }; struct Bar { public: Bar() { detail::c_api(lib_bar_create, &handle_); } ~Bar() { detail::c_api(lib_bar_destroy, handle_); } int func() { return detail::c_api<int>(lib_bar_func, handle_); } private: void * handle_; }; } // namespace lib
问题描述
作为API提供者,我们用C17编译上述代码,但客户端使用C11调用该API,请问是否会违反lib::detail::c_api的ODR(单一定义规则)?个人认为不会,因为lib_bar_func定义在客户端代码外的翻译单元,但对此不确定,特寻求解答。
解答:不会违反ODR
原因如下:
- 模板实例化的隔离性:
lib::detail::c_api是模板函数,它的实例化在每个翻译单元中独立进行。提供者的C17编译只会实例化自身代码中用到的c_api版本(比如lib_bar_func调用对应的实例),客户端的C11编译则会实例化自己代码中用到的c_api版本(比如Foo、Bar成员函数调用对应的实例)。这两组实例分属不同的翻译单元集合,彼此没有链接层面的交互。 - C API的边界隔离:共享库对外暴露的是C链接的函数,这些函数的实现完全在提供者的翻译单元中完成。客户端仅调用这些C接口,不会直接链接到提供者编译的
c_api实例;客户端自行编译的c_api实例仅用于调用C接口,和提供者内部的c_api实例完全独立。 - ODR的适用范围:ODR要求同一实体在所有使用它的翻译单元中定义一致,但这里提供者内部的
c_api实例和客户端的c_api实例是不同实体——它们是模板针对不同调用场景的实例化,分别存在于提供者的共享库和客户端的可执行文件/库中,互不干扰。
简言之,提供者的c_api实例仅在共享库内部生效,客户端的c_api实例仅在客户端代码中生效,两者通过C接口交互,不存在同一实体在不同翻译单元定义不一致的情况,因此不会违反ODR。
内容的提问来源于stack exchange,提问作者Matt Eding
相关产品推荐
相关产品推荐

