通过不带参数的指针调用函数[重复]

问题描述 投票:0回答:1

我想实现一个返回默认值的通用函数,然后通过函数指针使用它来替换具有不同原型的其他函数。

例如:

int replacement() {
  return 43;
}

int (*abstract)(int i, const char *c);

int main() {
    abstract = reinterpret_cast<decltype(abstract)>(replacement);
    int res = (*abstract)(55, "Test");

    std::cout << "Result is " << res << std::endl;
}

这段代码有效,并且实际上达到了我的预期,但根据标准,它是未定义的行为。或者是吗?

我可以依靠 ABI 来做正确的事吗?参数是在寄存器中传递或压入堆栈的,那么如果不使用它们,应该没有问题。例如,在 GTK+ 和 Vulkan 中使用

reinterpret_cast
或其他等效的强制转换来将通用函数类型作为指针传递,并让开发人员在另一端取消强制转换正确的函数类型。

这只是偶然发生的,还是我可以依赖这种行为?

c++ function-pointers reinterpret-cast
1个回答
0
投票

它的工作纯属偶然。你永远不能依赖未定义的行为。

  1. 由于使用了 ABI,它可能适用于您的特定 CPU/操作系统组合。但代码是完全不可移植的。例如,在旧的 32 位 Win32 stdcall 约定中,它会损坏堆栈。
  2. 编译器可能总是在 UB 不发生的假设下优化代码。它证明指向的函数不符合调用签名?那么调用一定是死代码,或者赋值可能一定是真正的死代码。考虑:

.

int stub() { return 42; }
int real(int i) { return i * 2; }

void foo(bool active) {
 int (*ptr)(int);
  if (active) {
    ptr = &real;
  } else {
    ptr = reinterpret_cast<int (*)(int)>(&stub);
  }
  int arg = ptr(99);
  if (active) {
    submit_request(arg);
  }
}

编译器可以看到

active == false
导致 UB,因此在
foo
始终存在的假设下优化
active == true

(我尝试过的编译器都没有这样做,但这仅意味着他们还没有费心添加这样的优化。)

您应该这样做:可变参数模板可让您为您需要的任何签名定义存根:

#include <cstdio>

void submit_request(int);

template <typename... Args>
int stub(Args...) {
    std::printf("stub");
    return 42;
}

int real(int i) { return i * 2; }

void foo(bool active) {
    int (*ptr)(int);
    if (active) {
        ptr = &real;
    } else {
        ptr = static_cast<int (*)(int)>(&stub);
    }
    int arg = ptr(99);
    if (active) {
        submit_request(arg);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.