gpt4 book ai didi

c++ - 如何影响_calling_函数的值返回?

转载 作者:可可西里 更新时间:2023-11-01 18:17:13 26 4
gpt4 key购买 nike

我希望能够强制执行“双重返回”,即拥有一个强制从其调用 函数返回的函数(是的,我知道并不总是真正的调用函数等)显然我希望能够通过操纵堆栈来做到这一点,并且我认为至少以某种不可移植的机器语言方式是可能的。问题是这是否可以相对干净和便携地完成。

给个具体的代码来填,我要写函数

void foo(int x) {
/* magic */
}

使得下面的函数

int bar(int x) {
foo(x);
/* long computation here */
return 0;
}

返回,比方说,1;并且不执行长计算。假设 foo() 可以假设它只被带有 bar 签名的函数调用,即 int(int) (因此明确知道它的调用者返回类型是什么).

注意事项:

  • 请不要告诉我这是多么不好的做法,我是出于好奇才问的。
  • 调用函数(在示例中,bar())不得修改。它不会知道被调用的函数在做什么。 (同样在示例中,只能修改 /* magic */ 位)。
  • 如果有帮助,您可以假设没有发生内联(这可能是一个不切实际的假设)。

最佳答案

The question is whether this can be done relatively cleanly and portably.

答案是不能。

除了调用栈如何在不同系统上实现的所有不可移植的细节之外,假设 foo 被内联到 bar 中。然后(通常)它不会有自己的堆栈框架。您不能清楚地或可移植地谈论对“双倍”或“n 次”返回进行逆向工程,因为实际的调用堆栈不一定看起来像您基于 C 或 C++ 抽象调用所期望的那样机器。

破解此信息所需的信息可能(不保证)与调试信息一起可用。如果调试器要向其用户呈现“逻辑”调用堆栈,包括内联调用,则必须有足够的可用信息来定位“向上两级”调用者。然后你需要模仿平台特定的函数退出代码以避免破坏任何东西。这需要恢复中间函数通常会恢复的任何内容,即使使用调试信息也可能不容易弄清楚,因为执行此操作的代码位于 bar 某处。但我怀疑既然调试器可以显示那个调用函数的状态,那么至少原则上调试信息可能包含足够的信息来恢复它。然后返回到原始调用者的位置(这可以通过显式跳转来实现,或者通过操纵平台保留其返回地址并执行正常返回的任何位置来实现)。所有这些都非常肮脏且非常不可移植,因此我的回答是“否”。

我假设您已经知道可以移植地使用异常或 setjmp/longjmpbarbar 的调用者(或两者)都需要与之合作,并同意 foo 如何“返回值“被储存了。所以我认为这不是你想要的。但是如果修改 bar 的调用者是可以接受的,你可以做这样的事情。它不是很漂亮,但它几乎可以工作(在 C++11 中,使用异常)。我会留给你弄清楚如何在 C 中使用 setjmp/longjmp 并使用固定函数签名而不是模板:

template <typename T, typename FUNC, typename ...ARGS>
T callstub(FUNC f, ARGS ...args) {
try {
return f(args...);
}
catch (EarlyReturnException<T> &e) {
return e.value;
}
}

void foo(int x) {
// to return early
throw EarlyReturnException<int>(1);
// to return normally through `bar`
return;
}

// bar is unchanged
int bar(int x) {
foo(x);
/* long computation here */
return 0;
}

// caller of `bar` does this
int a = callstub<int>(bar, 0);

最后,不是“错误做法讲座”,而是实际警告——使用任何技巧提前返回通常不适用于用 C 编写的代码或用 C++ 编写的代码不要期望异常离开 foo。原因是 bar 可能已经分配了一些资源,或者在调用 foo 之前将某些结构置于违反其不变量的状态,目的是释放该资源或恢复调用后的代码不变。因此,对于一般函数 bar,如果您跳过 bar 中的代码,则可能会导致内存泄漏或无效数据状态。一般来说,无论 bar 中有什么,避免这种情况的唯一方法是允许 bar 的其余部分运行。当然,如果 bar 是用 C++ 编写的,并期望 foo 可能抛出异常,那么它将使用 RAII 作为清理代码,并在您抛出异常时运行。不过,longjmp对析构函数的处理具有未定义的行为,因此您必须在开始之前决定是处理 C++ 还是 C。

关于c++ - 如何影响_calling_函数的值返回?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20870565/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com