5

与我之前的问题有点相关

有没有办法从 d 中的函数或方法中获取调用对象?

例子:

class Foo
{
    public void bar()
    {
        auto ci = whoCalledMe();
        // ci should be something that points me to baz.qux, _if_ baz.qux made the call

    }
}

class Baz
{
    void qux()
    {
        auto foo = new Foo();
        foo.bar();
    }
}

问题:

  1. 类似的东西whoCalledMe存在吗?如果是这样,它叫什么?
  2. 如果确实存在某些东西,是否可以在编译时(在模板中)使用它,如果是,如何使用?

或者;

  1. 是否可以在运行时访问调用堆栈?像 php 的debug_backtrace
4

3 回答 3

5

为了扩展Cyber​​Shadow 所说的内容,由于您可以通过 using 获取函数的完全限定名称,因此__FUNCTION__您还可以使用 mixin 将函数作为符号获取:

import std.stdio;
import std.typetuple;

void callee(string file=__FILE__, int line=__LINE__, string func=__FUNCTION__)()
{
    alias callerFunc = TypeTuple!(mixin(func))[0];
    static assert(&caller == &callerFunc);

    callerFunc();  // will eventually overflow the stack
}

void caller()
{
    callee();
}

void main()
{
    caller();
}

堆栈将在此处溢出,因为这两个函数最终会无限递归地相互调用。

于 2013-08-13T19:25:45.253 回答
4

无法直接获取有关“来电者”的信息。您可能会从调用堆栈中获得地址,但这是一个低级操作,并且取决于诸如您的程序是否使用堆栈帧编译之类的事情。获得地址后,理论上可以将其转换为函数名称和行号,前提是程序的二进制文件可以使用调试符号,但是(再次)这是高度特定于平台的,并且取决于用于编译程序的工具链.

作为替代方案,您可能会发现这很有帮助:

void callee(string file=__FILE__, int line=__LINE__, string func=__FUNCTION__)()
{
    writefln("I was called by %s, which is in %s at line %d!", func, file, line);
}

void caller()
{
    // Thanks to IFTI, we can call the function as usual.
    callee();
}

但请注意,您不能对非最终类方法使用此技巧,因为每次调用该函数都会生成一个新的模板实例(并且编译器需要事先知道一个类的所有虚拟方法的地址)。

于 2013-08-13T14:22:24.957 回答
2

查找调用者是调试器所做的事情,通常需要在构建程序时打开符号调试信息开关。阅读调试信息来解决这个问题是高度依赖于系统的,并且非常先进。

异常展开机制也会找到调用者,但那些表不会为不需要它们的函数生成,并且表中不包含函数的名称。

于 2013-08-15T07:37:24.497 回答