11

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

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

class A { }

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

我可以

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

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

相关问题:

4

3 回答 3

8

为此,我编写了 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);
        }
    };
于 2014-10-24T16:49:03.630 回答
5

如果您查看 C# lambda 的反编译,您会发现 C# 编译器与您的选项 #2 执行相同的操作。创建一堆一次性类很烦人,但这是我推荐的。

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

于 2013-07-31T20:27:02.773 回答
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); });
于 2016-06-15T19:40:53.973 回答