16

我正在尝试与std::function结合使用std::bind,但我遇到了一些问题。

这有效:

#include <functional>
#include <iostream>

void print() {
    std::cout << 2;
}

int main() {
    std::function<void ()> foo = print;
    (*foo.target<void (*)()>())(); //prints 3
}

这在第二行崩溃main

#include <functional>
#include <iostream>

void print (int i) {
    std::cout << i;
}

int main() {
    std::function<void ()> foo = std::bind (print, 2);
    (*foo.target<void (*)()>())();
}

我真的持有std::function<void ()>并且需要能够返回该功能;不只是调用它。我希望用法是这样的:

#include <functional>
#include <iostream>

void print (int i) {
    std::cout << i;
}

int main() {
    Container c (std::bind (print, 2));

    //I would expect the original
    c.func() (3); //prints 3

    if (c.func() == print) /* this is what I'm mostly getting at */
}

有没有办法让原始函数返回它,或者替代方法?它也确实与返回类型发生冲突,因为它void (*)()与绑定签名非常匹配。

4

4 回答 4

30

这是完全不可能的。存在的全部原因std::function是函数指针非常糟糕,永远不应该被任何人使用,除了那些注定要承受地狱C 互操作标准的注定要失败的灵魂,因为它们无法处理带有状态的函数。

在一般情况下, Astd::function<void()>不能转换为 a void(*)()。这在第一个示例中起作用的唯一原因是因为它恰好void(*)()原始的。

于 2012-06-07T19:46:55.613 回答
16

这可以使用一点模板元编程来实现。我最近在围绕 OpenGL GLUT(取决于回调函数指针)编写通用 C++ 包装器时使用了它。该方法:

  1. 实例化单例模板类型的实例。
  2. 将您的 std::function 存储为单例实例的成员
  3. 通过静态成员函数调用您的 std::function(静态成员函数和自由函数具有相同的类型,因此“调用”函数可以用作自由函数指针)

在 GCC 4.8 上的 C++11下测试。

#include <unistd.h>
#include <thread>
#include <chrono>
#include <mutex>
#include <functional>
#include <iostream>
#include <cmath>

template <const size_t _UniqueId, typename _Res, typename... _ArgTypes>
struct fun_ptr_helper
{
public:
    typedef std::function<_Res(_ArgTypes...)> function_type;

    static void bind(function_type&& f)
    { instance().fn_.swap(f); }

    static void bind(const function_type& f)
    { instance().fn_=f; }

    static _Res invoke(_ArgTypes... args)
    { return instance().fn_(args...); }

    typedef decltype(&fun_ptr_helper::invoke) pointer_type;
    static pointer_type ptr()
    { return &invoke; }

private:
    static fun_ptr_helper& instance()
    {
        static fun_ptr_helper inst_;
        return inst_;
    }

    fun_ptr_helper() {}

    function_type fn_;
};

template <const size_t _UniqueId, typename _Res, typename... _ArgTypes>
typename fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::pointer_type
get_fn_ptr(const std::function<_Res(_ArgTypes...)>& f)
{
    fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::bind(f);
    return fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::ptr();
}

template<typename T>
std::function<typename std::enable_if<std::is_function<T>::value, T>::type>
make_function(T *t)
{
    return {t};
}

int main()
{
    std::cout << (void*)get_fn_ptr<0>(make_function(::sin))<<std::endl;
    return 0;
}
于 2013-08-24T20:35:10.693 回答
6

您无法从 中获取函数指针std::function因为甚至可能没有。它可以是一个成员函数指针,也可以是一个实现operator().

于 2012-06-07T19:46:14.127 回答
1

这通常是不可能的但大多数 C API 都有一个“上下文”指针,您可以将其与 C 函数指针一起传递。所以对于互操作,你可以用这样的东西来包装它(假设 API 首先传递这个 'context' 参数):

template <class R, class... Args>
struct CFunctionPointer
{
    std::function<R(Args...)> func;

    static R callback(void* context, Args... args)
    {
        const CFunctionPointer* unpackedThis = reinterpret_cast<const CFunctionPointer*>(context);
        return unpackedThis->func(std::forward<Args>(args)...);
    }
};

然后会这样调用:

auto callable = CFunctionPointer<void, const uint8_t*, const uint8_t*>{ [](const uint8_t* begin, const uint8_t* end) { std::cout << "Hello world\n"; } };
cfunc(..., &callable, &callable.callback);

可悲的是两次重复参数类型。

于 2021-01-21T10:36:45.547 回答