为什么这个Objective-C++的dynamic_castdebug成功,release失败?
Why does this dynamic_cast from Objective-C++ succeed in debug but fail in release?
我在最新版本的 Xcode(撰写本文时为 9.4.1)中构建了一个 C++ 框架,我正在使用 Objective-C++ 代码,再次在 Xcode。我需要执行从一种指针类型到另一种指针类型的 dynamic_cast
。但是,dynamic_cast
仅适用于调试版本,不适用于发布版本。关于 dynamic_cast
如何在 Objective-C++ 中工作,我是否缺少或理解导致此示例失败的某些内容?
C++ 框架
TestClass.hpp
class Parent {
public:
//
// must have at least 1 virtual function for RTTI
virtual ~Parent();
Parent() {}
};
class Child : public Parent {
public:
// if you put the implementation for this func
// in the header, everything works.
static Child* createRawPtr();
};
TestClass.cpp
#include "TestClass.hpp"
Parent::~Parent() {}
Child* Child::createRawPtr() {
return new Child;
}
Objective-C++ 命令行应用程序
main.mm
#import <Foundation/Foundation.h>
#import <TestCastCPP/TestClass.hpp>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Parent *parentPtr = Child::createRawPtr();
Child *child = dynamic_cast<Child*>(parentPtr);
NSLog(@"Was the cast successful? %s", child != nullptr ? "True" : "False");
}
return 0;
}
在 Debug 和 Release 中,我希望此代码打印 "True"。然而,实际上,Release 模式打印 "False"。作为冒烟测试,this SO post 处的 dynamic_cast
工作正常。
有趣的是,同样类型的代码在 C++ 命令行应用程序中运行,同样在 Xcode 中。我试过在 Release 模式下禁用优化器,但这似乎并没有解决问题。
我有一个示例项目up on GitHub here。记得在Release里编译一下看看我问题的原因。我已经包含了 Objective-C++ 的 TestCast
方案,以及直接 C++ 的 TestCastCPP
方案。
很难知道编译器的细节,因为编译器如何执行 RTTI 具有一定的灵活性(即,规范没有详细说明)。
在这种情况下,由于 Child class 没有定义任何虚函数,我怀疑编译器已经为 Child class.
的每个翻译单元发出了 RTTI
当框架被 linked 时,当可执行文件被 linked 时,每个都有自己的子 RTTI 信息,因为每个翻译单元发出自己的 RTTI。
一个的父 link 与另一个的父 link 不匹配,我怀疑,所以他们没有相同的父指针,那些东西不是 "fixed up" 由动态加载程序。 (dynamic_cast<Child*>
基本上遍历父指针链,直到它通过指针值而不是 RTTI 值找到匹配项。)
如果您查看了应用程序和框架的 nm -g TestCast | c++filt
转储,您可以看到 RTTI 块。拆开来看,我认为Child RTTI在这两种情况下都已经解析为它们自己的Parent RTTI。
为什么它对 DEBUG 有效,对 RELEASE 无效?发布优化之一可能是根据使用情况去除外部 linkage 符号的死代码。因此 DEBUG 的动态加载程序 (dyld) 能够解析符号,但是 RELEASE 构建一个或多个符号已经在内部解析。
可能有一种方法可以指示应保留和导出 RTTI 的 "unused" 符号,这将因 compiler/linker 而异。但这比提供避免问题的显式 "first virtual function"(例如虚拟 Child 析构函数)更麻烦。
我在最新版本的 Xcode(撰写本文时为 9.4.1)中构建了一个 C++ 框架,我正在使用 Objective-C++ 代码,再次在 Xcode。我需要执行从一种指针类型到另一种指针类型的 dynamic_cast
。但是,dynamic_cast
仅适用于调试版本,不适用于发布版本。关于 dynamic_cast
如何在 Objective-C++ 中工作,我是否缺少或理解导致此示例失败的某些内容?
C++ 框架
TestClass.hpp
class Parent {
public:
//
// must have at least 1 virtual function for RTTI
virtual ~Parent();
Parent() {}
};
class Child : public Parent {
public:
// if you put the implementation for this func
// in the header, everything works.
static Child* createRawPtr();
};
TestClass.cpp
#include "TestClass.hpp"
Parent::~Parent() {}
Child* Child::createRawPtr() {
return new Child;
}
Objective-C++ 命令行应用程序
main.mm
#import <Foundation/Foundation.h>
#import <TestCastCPP/TestClass.hpp>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Parent *parentPtr = Child::createRawPtr();
Child *child = dynamic_cast<Child*>(parentPtr);
NSLog(@"Was the cast successful? %s", child != nullptr ? "True" : "False");
}
return 0;
}
在 Debug 和 Release 中,我希望此代码打印 "True"。然而,实际上,Release 模式打印 "False"。作为冒烟测试,this SO post 处的 dynamic_cast
工作正常。
有趣的是,同样类型的代码在 C++ 命令行应用程序中运行,同样在 Xcode 中。我试过在 Release 模式下禁用优化器,但这似乎并没有解决问题。
我有一个示例项目up on GitHub here。记得在Release里编译一下看看我问题的原因。我已经包含了 Objective-C++ 的 TestCast
方案,以及直接 C++ 的 TestCastCPP
方案。
很难知道编译器的细节,因为编译器如何执行 RTTI 具有一定的灵活性(即,规范没有详细说明)。
在这种情况下,由于 Child class 没有定义任何虚函数,我怀疑编译器已经为 Child class.
的每个翻译单元发出了 RTTI当框架被 linked 时,当可执行文件被 linked 时,每个都有自己的子 RTTI 信息,因为每个翻译单元发出自己的 RTTI。
一个的父 link 与另一个的父 link 不匹配,我怀疑,所以他们没有相同的父指针,那些东西不是 "fixed up" 由动态加载程序。 (dynamic_cast<Child*>
基本上遍历父指针链,直到它通过指针值而不是 RTTI 值找到匹配项。)
如果您查看了应用程序和框架的 nm -g TestCast | c++filt
转储,您可以看到 RTTI 块。拆开来看,我认为Child RTTI在这两种情况下都已经解析为它们自己的Parent RTTI。
为什么它对 DEBUG 有效,对 RELEASE 无效?发布优化之一可能是根据使用情况去除外部 linkage 符号的死代码。因此 DEBUG 的动态加载程序 (dyld) 能够解析符号,但是 RELEASE 构建一个或多个符号已经在内部解析。
可能有一种方法可以指示应保留和导出 RTTI 的 "unused" 符号,这将因 compiler/linker 而异。但这比提供避免问题的显式 "first virtual function"(例如虚拟 Child 析构函数)更麻烦。