![](/img/trans.png)
[英]Why does dynamic_cast give nullptr with private inheritance here?
[英]abi::__dynamic_cast returns nullptr for upcasts
我需要像這樣連接到 C++ 的異常拋出機制:
namespace __cxxabiv1
{
extern "C" void __cxa_throw(void* voidPointerToActualObject, std::type_info* stdTypeInfoOfActualObject, void (*destructor)(void *))
{
// If thrownException is a custom exception type or something deriving from it, poke a value into it.
}
}
我有一個拋出異常的簡單示例,它是非常簡單的 class 層次結構的一部分:
#include <stdexcept>
class Upper : public std::exception
{
public:
int pokeMe = 111111;
};
class Lower : public Upper {};
int main()
{
throw Lower();
}
#include <cxxabi.h>
namespace __cxxabiv1
{
extern "C" void __cxa_throw(void* voidPointerToActualObject, std::type_info* stdTypeInfoOfActualObject, void (*destructor)(void *))
{
// The point is to do the equivalent of this:
Lower* staticallyTypedPointerToActualObject = reinterpret_cast<Lower*>(voidPointerToActualObject);
auto thisWorks = dynamic_cast<Upper*>(staticallyTypedPointerToActualObject);
thisWorks->pokeMe = 222222;
// But we don't know the actual static type, so we can't get a statically typed pointer. We only have a void* and a type_info:
auto abiTypeInfoOfActualObject = dynamic_cast<const abi::__class_type_info*>(stdTypeInfoOfActualObject);
auto abiTypeInfoOfUpper = dynamic_cast<const abi::__class_type_info*>(&typeid(Upper));
Upper* thisDoesNotWork = reinterpret_cast<Upper*>(abi::__dynamic_cast(voidPointerToActualObject, abiTypeInfoOfActualObject, abiTypeInfoOfUpper, -1));
thisDoesNotWork->pokeMe = 333333;
// Elided for clarity: Call the original __cxa_throw function here
// Instead, suppress these warnings:
(void)destructor; // Unused parameter
while (1) { } // Return from non-returning function
}
}
我看不出__dynamic_cast
不能向上轉型的原因,但它返回nullptr
。
為什么? 我如何讓它工作?
它似乎能夠很好地進行向下轉換,順便說一句:
auto abiTypeInfoOfActualObject = dynamic_cast<const abi::__class_type_info*>(&typeid(Upper)); // Plonking this here for testing
auto abiTypeInfoOfUpper = dynamic_cast<const abi::__class_type_info*>(&typeid(Lower)); // Casting to Lower instead of Upper
Lower* thisDoesNotWork = reinterpret_cast<Lower*>(abi::__dynamic_cast(voidPointerToActualObject, abiTypeInfoOfActualObject, abiTypeInfoOfUpper, -1));
ABI 文檔不要求 __dynamic_cast 執行派生到基礎的轉換。 那些實際上可以由編譯器靜態執行的 __dynamic_cast 操作必須由編譯器靜態執行——運行時庫不希望在那種情況下被調用。
所以這回答了那個問題。 吃吧。
但談話中幸運地提到:
是的; 持有人知道 static 類型; 它可以拋出該類型的指針。 轉換操作可以捕獲它正在尋找的指針類型,或者使用 catch(...) 使轉換失敗。
這給了我嘗試這個(簡化版)的想法:
namespace __cxxabiv1
{
using ThrowFunction = decltype(__cxa_throw)*;
ThrowFunction oldThrowFunction = nullptr;
extern "C" void __cxa_throw(void* voidPointerToActualObject, std::type_info* stdTypeInfoOfActualObject, void (*destructor)(void *))
{
if (oldThrowFunction == nullptr)
{
oldThrowFunction = (ThrowFunction)dlsym(RTLD_NEXT, "__cxa_throw");
}
try
{
oldThrowFunction(voidPointerToActualObject, stdTypeInfoOfActualObject, destructor);
}
catch (Upper& ex)
{
ex.pokeMe = 333333;
}
catch (...)
{
}
oldThrowFunction(voidPointerToActualObject, stdTypeInfoOfActualObject, destructor);
}
}
我簡直不敢相信,但它確實有效!
編輯:免責聲明:似乎這樣,析構函數回調實際上被調用了兩次,因為如果使用std::string pokeMe
,當我第二次調用 oldThrowFunction 時,該字符串已被丟棄。 我將在接下來的幾天內進行試驗。
Edit2:確實如此。 我找不到任何表明__cxa_throw
是否接受nullptr
作為析構函數參數的信息(至少對我來說它沒有崩潰),所以最安全的選擇是傳遞一個指向空虛擬 function 的指針:
void dummyDestructor(void*)
{
}
//...
oldThrowFunction(voidPointerToActualObject, stdTypeInfoOfActualObject, &dummyDestructor);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.