【问题标题】:C++ trying to get function address from a std::functionC++ 试图从 std::function 获取函数地址
【发布时间】:2023-03-23 04:16:01
【问题描述】:

我正在尝试从 std::function 中查找函数的地址。

第一个解决方案是:

size_t getAddress(std::function<void (void)> function) {
    typedef void (fnType)(void);
    fnType ** fnPointer = function.target<fnType *>();
    return (size_t) *fnPointer;
}

但这仅适用于具有 (void ()) 签名的函数,因为我需要该函数 签名是 (void (Type &)),我试着做

template<typename T>
size_t getAddress(std::function<void (T &)> function) {
    typedef void (fnType)(T &);
    fnType ** fnPointer = function.target<fnType *>();
    return (size_t) *fnPointer;
}

我得到“错误 - 函数样式转换或类型构造的预期'('”

更新:有什么方法可以捕获成员类地址?对于我正在使用的班级成员:

template<typename Clazz, typename Return, typename ...Arguments>
size_t getMemberAddress(std::function<Return (Clazz::*)(Arguments...)> & executor) {
    typedef Return (Clazz::*fnType)(Arguments...);
    fnType ** fnPointer = executor.template target<fnType *>();
    if (fnPointer != nullptr) {
        return (size_t) * fnPointer;
    }
    return 0;
}

更新:捕获我正在使用的 lambda

template <typename Function>
struct function_traits
    : public function_traits<decltype(&Function::operator())> {
};

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
    typedef ReturnType (*pointer)(Args...);
    typedef std::function<ReturnType(Args...)> function;
};

template <typename Function>
typename function_traits<Function>::function
to_function (Function & lambda) {
    return static_cast<typename function_traits<Function>::function>(lambda);
}

template <typename Lambda>
size_t getAddress(Lambda lambda) {
    auto function = new decltype(to_function(lambda))(to_function(lambda));
    void * func = static_cast<void *>(function);
    return (size_t)func;
}

std::cout << getAddress([] { std::cout << "Hello" << std::endl;}) << std::endl;

【问题讨论】:

  • 似乎工作正常。您可能希望包含一个完整的示例,包括用法。显示实际失败的东西。
  • 除此之外:您假设(由于未定义行为的痛苦)std::functions 中的所有内容都是函数指针,这不是很明智,因为如果它们是,您可以使用函数指针而是键入,而不会遇到所有这些麻烦。

标签: c++ function pointers std


【解决方案1】:

调用target时需要使用template关键字:

#include <functional>
#include <iostream>

template<typename T>
size_t getAddress(std::function<void (T &)> f) {
    typedef void (fnType)(T &);
    fnType ** fnPointer = f.template target<fnType*>();
    return (size_t) *fnPointer;
}

void foo(int& a) {
    a = 0;
}

int main() {
    std::function<void(int&)> f = &foo;
    std::cout << (size_t)&foo << std::endl << getAddress(f) << std::endl;
    return 0;
}

提示:当你遇到 C++ 语法问题时,我建议你使用clang++ 来编译你的代码。如果您尝试编写代码的方式,它通常会为您指出编写方向以修复错误(当它可以弄清楚您在做什么时)。

我还建议您使用可变参数模板使您的函数更通用:

template<typename T, typename... U>
size_t getAddress(std::function<T(U...)> f) {
    typedef T(fnType)(U...);
    fnType ** fnPointer = f.template target<fnType*>();
    return (size_t) *fnPointer;
}

【讨论】:

  • 我目前正在使用 CLang 3.4 :D
  • 在这种情况下,如果您更改添加 typedef fnType* fnTypeP; 并将您对目标的调用更改为 target&lt;fnTypeP&gt;,clang 将告诉您添加 template 和位置。如果您使解析器的工作足够简单,它就可以弄清楚您要做什么:)
  • 等等,你能简单地将std::function 转换为函数指针吗?这也适用于有状态函数吗?它是如何工作的?
  • 我正在使用函数地址将它们存储到地图中并能够将其删除。您可以将 std::function 转换为 C 函数指针。
  • 不,你不能在所有情况下。例如,如果我将函数绑定到带有捕获的 lambda,target() 将返回一个空指针,并且该函数将出现段错误。谨慎使用。
【解决方案2】:

std::function 只是一个对象,尽管伪装成非对象。所以,我们可以获取这个对象的地址,这在副本等之间是不变的。

要获得指针,我们需要进行一些转换。例如,给定一个函数f1,我们可以通过以下方式打印隐式指针:

std::cout << "f1: " << *(long *)(char *)&f1 << std::endl;

这是做什么的:

  • 获取 f1 的地址。 f1 本身就是一个指向函数对象的指针,尽管它伪装成一个原语
    • 所以这个地址是指向函数指针的指针
    • 我们想要的是指向函数的底层指针
  • 将指向指针的指针转换为指向 char 的指针
  • 然后指向 long 的指针
  • 现在,假设指针大小为longs,我们可以将此指针取消引用为long,并获取与函数对象关联的底层地址。

给定两个std::function&lt;void()&gt;s f1f2,我们可以这样做:

std::cout << "f1: " << *(long *)(char *)&f1 << std::endl;
std::cout << "f2: " << *(long *)(char *)&f2 << std::endl;

std::function<void()> f1copy = f1;
std::cout << "\nafter copy f1 into f1copy:" << std::endl;
std::cout << "addresses of f1 and f1copy differ: " << &f1 << " " << &f1copy << std::endl;
std::cout << "but underlying pointers identical: " <<
    *(long *)(char *)&f1 << " " << *(long *)(char *)(&f1copy) << std::endl;

std::cout << "\n after assign f2 to f1copy" << std::endl;
f1copy = f2;
std::cout << "now the underlying pointer of f1copy matches f2's:" << std::endl;
std::cout <<
    *(long *)(char *)&f2 << " " << *(long *)(char *)&f1copy << std::endl;

示例输出:

f1: 4439003784
f2: 4439003912

after copy f1 into f1copy:
addresses of f1 and f1copy differ: 0x7fff572ab970 0x7fff572ab910
but underlying pointers identical: 4439003784 4439003784

 after assign f2 to f1copy
now the underlying pointer of f1copy matches f2's:
4439003912 4439003912

【讨论】:

  • 它有效,谢谢!但我必须承认这可能是不安全的,例如,如果编译器使用不同的实现,那么第一个成员可能不是指针,而只是填充或类似的东西(即使我不认为这可能会发生)。但还是不错的答案?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多