使用std :: bind时从std :: function获取函数指针

时间:2012-06-07 19:44:09

标签: c++ c++11 bind function-pointers

我正在尝试将std::functionstd::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 个答案:

答案 0 :(得分:24)

这是不可能的。 std::function存在的全部原因是函数指针可怕地吸吮,并且永远不应该被任何人使用,除了注定了地狱的燃烧标准 C互操作的注定灵魂之外,因为他们无法处理状态函数。

在一般情况下,std::function<void()>无法转换为void(*)()。这在第一个示例中起作用的唯一原因是因为发生原来是void(*)()

答案 1 :(得分:12)

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

  1. 实例化单例模板类型的实例。
  2. 将std :: function存储为单例实例的成员
  3. 通过静态成员函数调用你的std :: function(静态成员函数和自由函数具有相同的类型,所以“invoke”函数可以用作自由函数指针)
  4. C++11 on GCC 4.8下测试。

    #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;
    }
    

答案 2 :(得分:7)

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

答案 3 :(得分:0)

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

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);

遗憾地将参数类型复制了两次。