2010-07-28 8 views
10

Estoy tratando de usar una lambda para pasar en lugar de un puntero a la función, pero parece que VS2010 no puede convertirlo. He intentado usar std :: function como este y se bloquea y no tengo idea si lo estoy haciendo bien.C++ 0x Lambda para el puntero a la función en VS 2010

#include <windows.h> 
#include <conio.h> 

#include <functional> 
#include <iostream> 

#include <concrt.h> 


void main() 
{ 
    std::function<void(void*)> f = [](void*) -> void 
    { 
     std::cout << "Hello\n"; 
    }; 


    Concurrency::CurrentScheduler::ScheduleTask(f.target<void(void*)>(), 0); 

    getch(); 
} 

Parece extraño que el compilador no puede convertir un lambda como a un simple puntero de función, ya que recoge ninguna variable - también en el caso de que lo hizo me pregunto qué se puede hacer.

¿Es el tipo de cada lambda único? Entonces, ¿podría piratear con una función de plantilla usando el tipo lambdas como un argumento de plantilla para generar una función estática única que podría llamarse y optimizarse?

ACTUALIZADO

La continuación parece funcionar, pero ¿es seguro?

#include <windows.h> 
#include <conio.h> 

#include <iostream> 

#include <concrt.h> 


template<typename Signature> 
struct Bind 
{ 
    static Signature method; 

    static void Call(void* parameter) 
    { 
     method(parameter); 
    } 
}; 


template<typename Signature> 
Signature Bind<Signature>::method; 


template<typename Signature> 
void ScheduleTask(Signature method) 
{ 
    Bind<Signature>::method = method; 
    Concurrency::CurrentScheduler::ScheduleTask(&Bind<Signature>::Call,0); 
} 


void main() 
{ 
    ScheduleTask 
    ( 
     [](void*) 
     { 
      std::cout << "Hello"; 
     } 
    ); 


    ScheduleTask 
    ( 
     [](void*) 
     { 
      std::cout << " there!\n"; 
     } 
    ); 


    getch(); 
} 

actualizado de nuevo

Así que con la ayuda dado que he llegado con el más corto:

template<typename Signature> 
void (*LambdaBind(Signature))(void*) 
{ 
    struct Detail 
    { 
     static void Bind(void* parameter) 
     { 
      Signature method; 

      method(parameter); 
     } 
    }; 


    return &Detail::Bind; 
} 

Esto puede ser usado para envolver un lambda sin cierre de void(*)(void*) en el puntero de función equivalente. Parece que esto será innecesario en una versión posterior de VS2010.

Entonces, ¿cómo hacer que esto funcione para una lambda con cierres?

¡ACTUALIZADO OTRA VEZ!

Obras para cierres en VS2010 - ni idea de si es 'seguro', aunque ...

template<typename Signature> 
struct Detail2 
{ 
    static std::function<void(void*)> method; 


    static void Bind(void* parameter) 
    { 
     method(parameter); 
    } 
}; 


template<typename Signature> 
std::function<void(void*)> Detail2<Signature>::method; 


template<typename Signature> 
void (*LambdaBind2(Signature method))(void*) 
{ 
    Detail2<Signature>::method = method; 
    return &Detail2<Signature>::Bind; 
} 
+0

[Related] (http://stackoverflow.com/questions/3321283/c0x-lambda-how-can-i-pass-as-a-parameter), no consideraría un duplicado. – GManNickG

+0

Ah, eso ayudó ... ¿y ahora tengo un trabajo que no creo que sea seguro en mi pregunta actualizada? – iam

+0

Parece lo suficientemente seguro. Te proporcionaré una solución más genérica. Debe eliminar 'Método de firma estático;' y en su lugar poner 'Método de firma;' dentro de la función. Está vacío, así que no costará nada de todos modos. – GManNickG

Respuesta

5

This feature of lambda's was added after VS2010 implemented them, so they don't exist in it yet.

Aquí hay una posible solución alternativa genérica, muy probada:

#include <functional> 
#include <iostream> 

namespace detail 
{ 
    // helper specializations, 
    // define forwarding methods 
    template <typename Lambda, typename Func> 
    struct lambda_wrapper; 

    #define DEFINE_OPERATOR \ 
      typedef decltype(&call) function_type; \ 
      operator function_type(void) const \ 
      { \ 
       return &call; \ 
      } 

    template <typename Lambda, typename C, typename R> 
    struct lambda_wrapper<Lambda, R (C::*)(void) const> 
    { 
     static R call(void) 
     { 
      Lambda x; 
      return x(); 
     } 

     DEFINE_OPERATOR 
    }; 

    template <typename Lambda, typename C, typename R, 
       typename A0> 
    struct lambda_wrapper<Lambda, R (C::*)(A0) const> 
    { 
     static R call(A0&& p0) 
     { 
      Lambda x; 
      return x(std::forward<A0>(p0)); 
     } 

     DEFINE_OPERATOR 
    }; 

    // and so on 
    #undef DEFINE_OPERATOR 
} 

// wraps a lambda and provides 
// a way to call it statically 
template <typename Lambda> 
struct lambda_wrapper : 
     detail::lambda_wrapper<Lambda, decltype(&Lambda::operator())> 
{}; 

template <typename Lambda> 
lambda_wrapper<Lambda> wrap_lambda(const Lambda&) 
{ 
    return lambda_wrapper<Lambda>(); 
} 

int main(void) 
{ 
    auto l = [](){ std::cout << "im broked :(" << std::endl; }; 
    std::function<void(void)> f = wrap_lambda(l); 

    f(); 
} 

Avíseme si alguna parte es confusa.

+0

Se ejecuta para leer en decltype y && ... – iam

+0

@wb: Heh, 'decltype' solo te da el tipo de expresión . Y '&&' son "referencias rvalue". – GManNickG

+0

Entonces, ¿cómo se puede hacer que esto funcione para una lambda con un cierre? PD: He actualizado mi pregunta con el código de una versión más corta para mi caso de firma específico. – iam

0

Si la programación de lambdas/objetos en función de concurrencia :: CurrentScheduler es lo que quiere, puede valer la pena mirar ConcRT v0.32 paquete de muestra here

La estructura task_scheduler puede programar lambdas de forma asíncrona, pero se aconseja , pasando por referencia puede provocar que sucedan cosas malas (ya que estamos hablando de programación asincrónica sin un join/wait, una referencia en la pila puede no ser válida en el momento de la ejecución de la tarea!)

Cuestiones relacionadas