c++ abi::__dynamic_cast为向上转换返回空指针

6qfn3psc  于 2023-02-01  发布在  其他
关注(0)|答案(1)|浏览(172)

我需要像这样连接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.
    }
}

If you're wondering "Why would you do that?"
我有一个抛出异常的简单例子,它是一个非常简单的类层次结构的一部分:

#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));
2wnc66cl

2wnc66cl1#

我找到了2004年的一段对话
ABI文档不要求__dynamic_cast执行派生到基类的转换。那些实际上可以由编译器静态执行的__dynamic_cast操作必须由编译器静态执行--运行时库不希望在这种情况下被调用。
这就回答了那个问题。
但谈话中幸运地提到:
是;保持器知道静态类型;它可以抛出该类型的指针。强制转换操作可以捕获它正在查找的指针类型,或者使用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时就被丢弃了,我将在接下来的几天里进行试验。

相关问题