没有可以捕获托管变量的lambdas的解决方法

时间:2013-07-31 16:23:48

标签: c++-cli

在C ++ / CLI中,您无法创建托管lambda(就像在C#中一样),因此您无法捕获托管变量。您可以创建常规方法(而不是lambdas),但仍然无法捕获托管变量。

是否有在C ++ / CLI代码中使用的标准解决方法?换句话说,我正在寻找一种可以在C ++ / CLI中使用的标准模式,以便从C#中执行以下操作:

class A { }

class B
{
    void Foo()
    {
        A a = new A();
        Func<A> aFunc = () => a; // Captures a
    }
}

我可以

  • 为我想要捕获的每个变量创建一个成员变量,然后在委托中使用该成员变量。这在一般情况下不起作用,因为您可能有两个方法的调用想要处理不同的捕获的a,但它适用于常见情况。
  • 创建一个嵌套类,在其ctor中执行捕获,然后使用此嵌套类的方法作为委托。这应该适用于一般情况,但这意味着每次我想捕获不同的变量时我都需要一个嵌套类。

问题:有没有比上述更好的选择,或者上面哪个选项是您的首选方法?

相关问题:

3 个答案:

答案 0 :(得分:7)

我为此目的编写了Lamda2Delegate结构。 实际上它将c ++ 11 lambda转换为任何.net委托。

使用示例:

    Thread^ TestLambaWrapper()
    {
        gcroot<String ^> str = "Testext";
        int i = 12345;
        Thread^ newThread = gcnew Thread(
            Lambda2Delegate<ParameterizedThreadStart>() = [&, str](Object ^ str2)
            {
                Sleep(2000);
                Console::WriteLine("Thread output = {0} {1} {2}", str, i, str2);
            }
        );
        newThread->Start("Nahnah");
        return newThread;
    }

对于你的情况:

    gcroot<A^> a = gcnew A();

    Func<A^> ^ aFunc = Lambda2Delegate<>() = [a](){ return (A^)a; };

    auto a2 = aFunc();

要捕获托管类,您需要使用gcroot包装它们,并通过值显式捕获。

Lambda2Delegate.h本身

    #pragma once
    #ifdef _MANAGED

    struct AutoDetectDelegateType {};

    template<typename TDelegate, typename TLambda, typename TRet, typename ...TParams>
    ref class LambdaHolder;

    template<typename TDelegate, typename TLambda, typename TRet, typename ...TParams>
    ref class LambdaHolder
    {
    public:
        inline LambdaHolder(const TLambda % func) { m_func = new TLambda(func); }
        !LambdaHolder() { delete m_func; }
        ~LambdaHolder() { !LambdaHolder(); }
    public:
        TRet Callback(TParams... params) { return (*m_func)(params...); }
        operator TDelegate ^ () { return gcnew TDelegate(this, &LambdaHolder::Callback); }
    private:
        TLambda * m_func;
    };

    template<typename TLambda, typename TRet, typename ...TParams>
    ref class LambdaHolder<AutoDetectDelegateType, TLambda, TRet, TParams...>
    {
    public:
        inline LambdaHolder(const TLambda % func) { m_func = new TLambda(func); }
        !LambdaHolder() { delete m_func; }
        ~LambdaHolder() { !LambdaHolder(); }
    public:
        TRet Callback(TParams... params) { return (*m_func)(params...); }
        template<typename TDelegate>
        operator TDelegate ^ () { return gcnew TDelegate(this, &LambdaHolder::Callback); }
    private:
        TLambda * m_func;
    };

    template <typename TDelegate, typename TLambda>
    struct get_labmda_holder : public get_labmda_holder < TDelegate, decltype(&TLambda::operator()) > {};

    template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
    struct get_labmda_holder < TDelegate, TRet(__clrcall TLambda::*)(TParams...) const >
    {
        typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
    };

    template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
    struct get_labmda_holder < TDelegate, TRet(__clrcall TLambda::*)(TParams...) >
    {
        typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
    };

    template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
    struct get_labmda_holder < TDelegate, TRet(__thiscall TLambda::*)(TParams...) const >
    {
        typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
    };

    template <typename TDelegate, typename TLambda, typename TRet, typename... TParams>
    struct get_labmda_holder < TDelegate, TRet(__thiscall TLambda::*)(TParams...)>
    {
        typedef LambdaHolder<TDelegate, TLambda, TRet, TParams...> TLambdaHolder;
    };

    template<typename TDelegate = AutoDetectDelegateType>
    struct Lambda2Delegate
    {
        template<typename TLambda>
        typename get_labmda_holder<TDelegate, TLambda>::TLambdaHolder ^ operator = (const TLambda % func)
        {
            return gcnew get_labmda_holder<TDelegate, TLambda>::TLambdaHolder(func);
        }
    };

    #endif

更新:无法在托管成员函数中声明c ++ lambda函数,但有解决方法 - 使用静态成员函数:

    ref class S
    {
    public:     
        int F(System::String ^ str)
        {
            return F(this, str);
        }
    private:
        //static function declaring c++ lambda
        static int F(S ^ pThis, System::String ^ str)
        {
            gcroot<System::String ^> localStr = "local string";
            System::Func<System::String ^, int> ^ func = Lambda2Delegate<>() = [=](System::String ^ str)
            {
                System::Console::WriteLine(str);
                System::Console::WriteLine(localStr);
                return str->Length;
            };
            return func(str);
        }
    };

答案 1 :(得分:3)

如果你看一下C#lambda的反编译,你会发现C#编译器与你的选项#2做同样的事情。创建一堆一次性类很烦人,但这就是我推荐的。

使用C#lambda,当它创建嵌套类实例时,它会使用遍布所有地方而不是局部变量。在编写使用嵌套类的方法时请记住这一点。

答案 2 :(得分:3)

这是我在C ++ / CLI中处理lambdas的解决方案,具有非常简单的语法。我以为其他人可能觉得它很有用:

struct DefaultDelegate;

template<typename... Args>
value struct DelegateType;

template<typename Ret, typename... Args>
value struct DelegateType<DefaultDelegate, Ret, Args...>
{
    delegate Ret MyDelegate(Args...);
    typedef MyDelegate delegate_type;
};

template<typename Target, typename Ret, typename... Args>
value struct DelegateType<Target, Ret, Args...>
{
    typedef Target delegate_type;
};

template<typename Lambda>
ref class LambdaWrapper
{
public:
    LambdaWrapper(Lambda &&lambda) : func(new Lambda(std::forward<Lambda>(lambda))) {}
    !LambdaWrapper() { delete func; }
    ~LambdaWrapper() { delete func; }
    template<typename Ret, typename... Args>
    Ret CallLambda(Args... args) { return (*func)(args...); }
private:
    Lambda *func;
};

template<typename Target, typename Lambda, typename Ret, typename... Args>
auto _toDelegate(Lambda &&lambda, Ret(Lambda::*func)(Args...))
{
    LambdaWrapper<Lambda> ^lw = gcnew LambdaWrapper<Lambda>(std::forward<Lambda>(lambda));
    return gcnew typename DelegateType<Target, Ret, Args...>::delegate_type(lw, &LambdaWrapper<Lambda>::CallLambda<Ret, Args...>);
}

template<typename Target, typename Lambda, typename Ret, typename... Args>
auto _toDelegate(Lambda &&lambda, Ret(Lambda::*func)(Args...) const)
{
    LambdaWrapper<Lambda> ^lw = gcnew LambdaWrapper<Lambda>(std::forward<Lambda>(lambda));
    return gcnew typename DelegateType<Target, Ret, Args...>::delegate_type(lw, &LambdaWrapper<Lambda>::CallLambda<Ret, Args...>);
}

template<typename Target, typename Lambda>
auto toDelegate(Lambda &&lambda)
{
    return _toDelegate<Target>(std::forward<Lambda>(lambda), &Lambda::operator());
}

用法:

int k = 2;
//If you need a generic delegate
Delegate ^d = toDelegate<DefaultDelegate>([k](int i, int j) ->int {return k * (i + j); });
//If you need a delegate of a specific type
MyDelegate ^d = toDelegate<MyDelegate>([k](int i, int j) ->int {return k * (i + j); });