2012-06-07 17 views
6

Estoy tratando de usar std::function junto con std::bind, pero estoy teniendo algunos problemas.Obtener el puntero a la función de std :: function cuando se utiliza std :: bind

Esto funciona:

#include <functional> 
#include <iostream> 

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

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

Esto se estrella en la segunda línea de 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 (*)()>())(); 
} 

realmente estoy sosteniendo el std::function<void()> y la necesidad de ser capaz de volver a la función; no solo llámalo. Espero que el uso sería algo como esto:

#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 */ 
} 

¿Hay alguna manera de obtener la función original para devolverlo, o una alternativa? También entra en conflicto con el tipo de devolución, ya que void (*)() coincide bastante bien con la firma encuadernada.

+0

Esto simplemente no es posible: no hay función con una firma 'void()' en su código. Si hubiera sido posible, no necesitaríamos 'std :: function'. –

+0

@ R.MartinhoFernandes, fue un buen truco para almacenar una función genérica, pero parece que solo funciona. – chris

+0

@ chris: ¿Es el objetivo de almacenar una función genérica? Comparar las funciones genéricas se vuelve * muy * complicado, y esto es solo la punta del iceberg. – Puppy

Respuesta

20

Esto es completamente imposible. La razón por la que std::function existe es que los indicadores de función son horribles y nunca, nunca, deben ser utilizados por nadie, nunca más, excepto por las almas condenadas que llevan interoperabilidad C, ya que no pueden manejar funciones con estado.

A std::function<void()> no puede, en el caso general, convertirse a void(*)(). La única razón por la que esto funciona en el primer ejemplo es porque ocurre como void(*)() originalmente.

+1

Supongo que tendré que olvidarme de compararlo entonces, gracias. – chris

+15

* los punteros a las funciones chupan * ... solo de la misma manera que 'int' apesta, o punteros sin formato ... son los bloques de bajo nivel en los que se construyen las construcciones de nivel superior. Sin punteros crudos e int no tendrías 'std :: string', sin punteros a función no tendrías' std :: function' ... –

+3

@David: No realmente. Puede implementar 'std :: function' sin ningún conocimiento de punteros de función. – Puppy

6

No se puede obtener un puntero a la función de un std::function, ya que puede que ni siquiera haya uno. Podría ser un puntero de función de miembro en su lugar, o un objeto que implementa operator().

9

Esto se puede lograr utilizando una pequeña meta-programación de plantillas. Recientemente tuve uso para esto mientras escribía un contenedor genérico de C++ alrededor de OpenGL GLUT (que depende de los punteros de función de devolución de llamada). El enfoque:

  1. Cree una instancia de un tipo de plantilla singleton.
  2. Almacene su función std :: como miembro de la instancia singleton
  3. Invoque su función std :: a través de una función miembro estática (las funciones estáticas miembro y las funciones libres tienen el mismo tipo, por lo que la función "invocar" puede se utilizará como un puntero de función libre)

Probado en 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; 
} 
+1

He hecho una pequeña modificación en esta solución, que es agregar un nombre de tipo para la identificación única. Emparejado con 'std :: enable_if' y' std :: is_literal' esto me permite usar enumeraciones u otros identificadores de tipo literal como la ID única, explorando más la unicidad y evitando colisiones (a costa de hacer que este código auxiliar sea un poco más) verboso). –

+0

Esta solución no funciona con multihilo cuando múltiples hilos establecen 'std :: function' de la clase singleton. –

Cuestiones relacionadas