10

I am trying to use a lambda to pass in place of a function pointer but VS2010 can't seem to convert it. I have tried using std::function like this and it crashes and I have no idea if I am doing this right!

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

It seems strange to me that the compiler can't convert such a lambda to a simple function pointer as it captures no variables - also in the case that it did I wonder what can be done.

Is the type of each lambda unique? So I could hack around with a template function using the lambdas' type as a template argument to generate a unique static function that could be called instead and hopefully optimised out?

UPDATED

The below seems to work but is it safe?

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

UPDATED AGAIN

So with the help given I have come up with the shorter:

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

            method(parameter);
        }
    };


    return &Detail::Bind;
}

This can be used to wrap a lambda with no closure of void(*)(void*) into the equivalent function pointer. It appears that this will become unnecessary in a later version of VS2010.

So how to get this to work for a lambda with closures?

UPDATED AGAIN!

Works for closures in VS2010 - no idea if it's 'safe' though...

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;
}
iam
  • 1,623
  • 1
  • 14
  • 28
  • [Related](http://stackoverflow.com/questions/3321283/c0x-lambda-how-can-i-pass-as-a-parameter), wouldn't consider a duplicate. – GManNickG Jul 28 '10 at 09:02
  • Ah that helped... and I have a work around now from that which I think is safe in my updated question? – iam Jul 28 '10 at 09:27
  • Seems safe enough. I'll provide you with a more generic solution. You should remove `static Signature method;` and instead put `Signature method;` within the function. It's empty so it won't cost anything anyway. – GManNickG Jul 28 '10 at 09:30
  • Actually I don't quite follow how you can do that? – iam Jul 29 '10 at 03:09
  • Ok now I get exactly what you mean.. thanks :) – iam Jul 29 '10 at 03:38
  • Updated into my original question – iam Jul 29 '10 at 07:15

2 Answers2

5

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

Here's a possible generic work-around, very untested:

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

Let me know if any part is confusing.

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • @wb: Heh, `decltype` just gives you the type of an expression. And `&&` are "rvalue references". – GManNickG Jul 29 '10 at 03:18
  • So how can this be made to work for a lambda with a closure? PS: I have updated my question with code for a shorter version for my specific signature case. – iam Jul 29 '10 at 07:16
  • @wb: Oops, sorry I missed your comments. Glad you got it solved though. :) – GManNickG Jul 30 '10 at 06:34
0

If scheduling lambdas/function objects in Concurrency::CurrentScheduler is what you want, it may be worth your while looking at ConcRT Sample Pack v0.32 here

The task_scheduler struct can schedule lambdas asynchronously, but be advised, passing by reference may cause bad things to happen (since we are talking about asynchronous scheduling without a join/wait, a reference on the stack may no longer be valid at time of task execution!)

NoNaMe
  • 6,020
  • 30
  • 82
  • 110
Vinod
  • 1