如何检测特定函数模板实例化并触发链接错误(无需修改目标TU)
某不可控第三方库通过ADL提供定制点,其默认实现对不可控类型another::C行为错误,因此我提供了该定制点的ADL实现。但其他代码(如other.cpp)可能在不包含我的实现的情况下调用库接口,引发ODR违反和行为不一致。
尝试全特化their::customization_point<another::C>以提供强符号覆盖,但如果库中their::interface<another::C>已内联错误的定制点调用,该方案失效。
现需寻求方法:无需修改目标翻译单元other.cpp,即可检测their::customization_point是否针对another::C实例化,若已实例化则触发构建阶段(编译/链接)错误。接受非可移植方案(偏好Linux下GCC),优先仅依赖编译器的方案(无需链接脚本)。
方案一:GCC __attribute__((error)) 编译阶段检测
利用GCC专属的error属性,通过模板特化强制在编译阶段抛出错误,直接阻止未包含正确ADL实现的代码编译。
实现代码
// 声明第三方库的定制点模板(仅需匹配声明,无需定义) namespace their { template<typename T> void customization_point(T&); } // 声明不可控类型 namespace another { class C; } // 特化第三方定制点并添加error属性:当该特化被实例化时,编译直接报错 template<> void their::customization_point<another::C>(another::C&) __attribute__((error("错误:未加载针对another::C的定制点正确实现,请包含our_adl_impl.h"))); // 正确的ADL实现放在our_adl_impl.h中: // namespace another { // void customization_point(C& c) { // // 你的正确实现逻辑 // } // }
原理
当任何翻译单元未包含our_adl_impl.h却实例化their::customization_point<another::C>时,编译器会匹配我们的特化版本,而该特化带有error属性,会直接在编译阶段输出指定错误信息并终止编译,从源头避免问题。
方案二:强符号冲突链接阶段检测
如果第三方库的接口是内联函数,导致方案一无法触发编译错误,可利用强符号冲突在链接阶段报错。
实现代码
// our_adl_impl.h 内容: #include <another/C.h> // 正确的ADL实现 namespace another { void customization_point(C& c) { // 你的正确实现逻辑 } } // 定义一个强符号,标记正确实现已加载 extern "C" void __our_adl_C_customization_loaded() {} // 特化第三方定制点,强制引用强符号 template<> void their::customization_point<another::C>(another::C&) { __our_adl_C_customization_loaded(); // 实际不会执行到这里(ADL会优先调用我们的实现),但未包含头文件时会触发符号引用 }
原理
未包含our_adl_impl.h的翻译单元实例化their::customization_point<another::C>时,会尝试引用__our_adl_C_customization_loaded这个强符号,但该符号仅在我们的头文件中定义,链接器会抛出undefined reference错误,阻止生成可执行文件。
内容的提问来源于stack exchange,提问作者ecatmur

