簡體   English   中英

abi::__dynamic_cast 為向上轉型返回 nullptr

[英]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));

我設法從 2004 年開始挖掘這個存檔的對話

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM