【问题标题】:stack exception handling and destructors unwinding. How to use the information堆栈异常处理和析构函数展开。如何使用信息
【发布时间】:2016-07-13 18:47:33
【问题描述】:

在我的程序中,我有一个垃圾收集器,我需要跟踪哪些对象保存在我的程序中的局部变量中,以避免在这些对象处于活动状态时对它们进行垃圾收集。

虽然我使用了链表来创建复合类型,但多年后我才意识到,C++ 语言必须保留该链表,以用于其自身的析构函数,尤其是与异常处理相关的析构函数。

所以我正在考虑通过使用 C++ 异常处理程序保存的信息来简化我的代码。有便携的方法吗?

如果没有,至少有关于 g++ 和 clang 的信息吗?

顺便说一下,当我使用多任务处理时,我应该能够为每个任务执行它(这些在垃圾收集器运行时正在等待)。

我真正需要的是遍历设置了析构函数的局部变量(并以非破坏性方式进行)。

【问题讨论】:

  • “在我的程序中我有一个垃圾收集器”- 停止。这已经是一个错误。 c++ 使用 RAII,这使得垃圾收集变得不必要。 Java 和 c# 有垃圾收集,这也是一个错误 - 但它们离纠正它的轨道太远了。

标签: c++ exception gcc


【解决方案1】:

通过检查 gnu gcc 编译器发出的代码,我发现有关要执行哪些析构函数的信息以代码形式“保存”,因为代码知道它必须破坏什么并处理它。所以要找回这些信息太难了。

实现可能有所不同,代码可能已作为返回地址、析构函数代码的地址以及结构实例的地址推入处理器堆栈。

所以我准备放弃努力,但我发现了另一条 hacky 路线。

我注意到 gnu 编译器在 16 字节的边界上对齐堆栈上的结构。 由于我的结构是 8 个字节,因此还有 8 个未使用的字节。 所以我想我可以使用它们来跟踪堆栈上分配的对象。 必须说,根据编译标志为-m64,-mx32,事情会发生变化,必须注意变化。

我设法让下面的代码在 -m64 和 -mx32 模式下编译和工作。

它不是纯粹的“C++”(恰恰相反),但代码可以工作并具有预期的效果。

#include <stdio.h>

#define ONSTACK(x) (((size_t)x)&0xf0000000)==0xf0000000
struct A;
void* P=nullptr;

struct A{
    static int i;
    long long a;
    A(){
        if(ONSTACK(this)){
            *(void**)(this+1)=P;
            P=(void*)(this+1);
        }
        printf("this=%p\n",this);
        a=0x1111111111111111*i;i++;
    }
    ~A(){
        if(ONSTACK(this))
            P=*(void**)(this+1);
            *(void**)(this+1)=nullptr;
        a=0xFFFFFFFFFFFFFFFF;}
};

int A::i=0xA;
void* p;

void r1(){
    A x;int xx=0x66666666;
    A y;int yy=0x77777777;
    A z;int zz=0x88888888;
    int b=0x2222222222222222;
    printf("hello world %p\n",&b);

    for(void* i=P;i!=nullptr;i=*(void**)i){
        printf("a=%llX\n",(((A*)i)-1)->a);
    }
}

void r2(){
    long long r2a;
    r2a=0x3333333333333333;
    r1();
}

A A0;

int main(int argc, char **argv)
{   int i;p=(char*)&i-0x100;
    printf("sizeof(P)=%i\n",sizeof(P));
    r2();
}

【讨论】:

    【解决方案2】:

    这就是析构函数的用途。如果您需要知道一个对象(无论是在堆上还是在堆栈上)何时超出范围,那么您可以为此使用析构函数。

    【讨论】:

    • 正如我已经说过的,我已经使用了一个带有链表和附加析构函数的复合对象。问题是 C++ 异常处理代码已经以某种方式保存了相同的信息。所以我想使用这些信息来避免:效率下降、与将对象声明为简单类型而不是复合类型相关的错误以及编码的简单性。
    • C++ 异常处理代码使用此信息来销毁对象并调用析构函数。典型的 C++ 编译器将其实现为 元组的单链表,指向上一个堆栈帧,以及调用已编译析构函数的代码地址。这在 C++ 中不能直接使用,正如我所写的,正确使用这些信息的方法是正确实现你的析构函数。
    猜你喜欢
    • 1970-01-01
    • 2014-02-16
    • 2013-11-07
    • 2015-07-03
    • 2017-12-06
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多