C ++ 0x лямбда-указатель на функцию в VS 2010 - PullRequest
10 голосов
/ 28 июля 2010

Я пытаюсь использовать лямбду для передачи вместо указателя на функцию, но VS2010 не может преобразовать ее.Я попытался использовать std :: function, как это, и она вылетает, и я понятия не имею, правильно ли я делаю это!

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

Мне кажется странным, что компилятор не может преобразовать такую ​​лямбду впростой указатель на функцию, поскольку он не захватывает переменные - также в том случае, если это действительно так, я задаюсь вопросом, что можно сделать.

Является ли тип каждой лямбды уникальным?Поэтому я мог бы взломать шаблонную функцию, используя тип lambdas в качестве аргумента шаблона, чтобы сгенерировать уникальную статическую функцию, которую можно было бы вызывать вместо этого и, мы надеемся, оптимизировать ее?

Кажется, что ниже работает, но безопасно ли это?

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

ОБНОВЛЕНО СНОВА

Итак, с помощью данной информации я придумал более короткое:

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

            method(parameter);
        }
    };


    return &Detail::Bind;
}

Это может использоваться, чтобы обернуть лямбду без замыкания void(*)(void*) в эквивалентный указатель на функцию.Похоже, что это станет ненужным в более поздней версии VS2010.

Так, как заставить это работать для лямбды с замыканиями?

ОБНОВЛЕНО СНОВА!

Работает для замыканий в VS2010 - не знаю, насколько это "безопасно", хотя ...

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

Ответы [ 2 ]

5 голосов
/ 28 июля 2010

Эта функция лямбды была добавлена ​​после того, как VS2010 реализовал их, поэтому в ней их пока нет.

Вот возможный общий обходной путь, очень непроверенный:

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

Дайте мне знать, если какая-то часть сбивает с толку.

0 голосов
/ 12 октября 2010

Если вы хотите запланировать лямбда-функции / объекты функций в Concurrency :: CurrentScheduler, возможно, вам стоит взглянуть на пакет образцов ConcRT v0.32 здесь

Структура task_scheduler может планировать лямбда-выражения асинхронно, но имейте в виду, что передача по ссылке может вызвать плохие вещи (поскольку речь идет об асинхронном планировании без соединения / ожидания, ссылка на стек может больше не будет действительным во время выполнения задачи!)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...