|
| 旧写法(BCC32) | 新写法(Clang 推荐) |
|---|---|
cdecl, _cdecl |
__cdecl |
pascal, _pascal |
__pascal |
_fastcall |
__fastcall(现在遵循 Microsoft 风格) |
_stdcall |
__stdcall |
_fortran, __fortran |
已废弃,不支持 |
asm, _asm, __asm |
内联汇编语法不同(见下文) |
_export |
__export |
_import |
__import |
_declspec |
__declspec |
Clang 转向使用标准的 C++11 属性语法(例如 [[noreturn]]、[[final]]),因此不再支持 BCC32 特有的自定义属性语法。
🛠️ 替代方案:《C++11 属性的 Clang 兼容写法》。
虽然 Clang 可以很好地处理 Unicode 字符串字面量和包含 Unicode 字符的文件名,但有一个硬性规定:标识符(变量名、函数名、类名等)必须使用 ASCII 字符集。
Clang 支持内联汇编,但有严格限制,与 BCC32 的体验大不相同:
为什么这些限制如此严格?这主要源于 pdata(程序展开数据) 的生成机制。Clang 编译器依赖 pdata 来实现可靠的异常回退和调试器栈回溯。然而,内联汇编会破坏 pdata 的生成,因为编译器无法分析和推断汇编代码对栈帧的具体操作。
📚 技术背景:
pdata是 Windows x64 平台上结构化异常处理(SEH)的核心机制。
.asm 文件(使用 MASM 或 NASM 汇编器),然后将其作为目标文件链接到你的 C++ 项目中。这是一个至关重要且容易混淆的差异,关乎程序的健壮性。为什么Clang不再支持捕获异步异常?
同步异常:这是 C++ 标准设计用于处理的异常,由 throw 关键字显式抛出。
异步异常:这类异常由操作系统触发,例如除以零、访问违规内存等,它们理论上可以在任何指令执行时发生。
BCC32 + RTL:通过其运行时库的扩展,能够捕获(部分)异步异常。
Clang:严格遵循 C++ 标准,仅支持同步异常。更重要的是,如果编译器检测到一个 try 块内部没有任何可能抛出 C++ 异常的函数调用或 throw 语句,它会直接优化掉(忽略)与之配套的 catch 或 __finally 块。
#include <iostream>
#include <float.h> // 可选:用于控制浮点异常
float a = 1.0f;
float b = 0.0f;
try {
try {
a = a / b; // ❗ 这不会抛出 C++ 异常,也不会立即触发 SEH 异步异常!
// 结果是 +inf(正无穷),符合 IEEE 754 标准。
}
__finally {
// ✅ 实际上会执行!
// 因为没有发生 SEH 异常,控制流正常到达此处。
std::cout << "Finally block executed." << std::endl;
}
}
catch (...) {
// ❌ 永远不会执行,因为没有 C++ 异常被抛出。
std::cout << "Caught an exception!" << std::endl;
}
throw 可能性,然后从 try 块中调用这个函数。⚠️ 如果不处理异步异常,程序在遇到严重错误(如访问违规)时,将触发 External Exception EEFFACE 并直接崩溃,你的
catch(...)将无能为力。
如果你在项目中使用过早期(Office XP 时代)的自动化接口单元(例如 ComObj.hpp 中的某些旧版封装),需要注意这些单元在 Clang 编译器中不可用。
✅ 建议:升级到 Office 2010 或更高版本的自动化接口库,这不仅能解决兼容性问题,通常还能获得更好的功能支持和性能。
从 BCC32 迁移到 Clang 增强型编译器,不仅仅是一次编译器架构的升级,更是将你的代码库推向现代 C++ 标准的重要一步。虽然初期可能需要对代码风格、构建配置甚至部分逻辑进行一些调整,但由此换来的更强的标准合规性、更好的跨平台潜力以及更可预测的运行时行为,无疑是值得的。
🛠️ 迁移口诀:
路径要规范,宏要更新,汇编慎用,异常分清,属性标准化,标识用 ASCII!
希望这份指南能帮助你更顺畅地完成迁移。如果在迁移中遇到具体问题,欢迎在云栈社区与广大开发者一同探讨。