Lambda 表达式作为 Visual C++ 中的 CLR (.NET) 委托/事件处理程序; 2010年

发布于 2024-08-31 15:19:11 字数 347 浏览 6 评论 0原文

是否可以使用 Visual C++ 2010 中的新 lambda 表达式作为 CLR 事件处理程序?我尝试过以下代码:

SomeEvent += gcnew EventHandler(
    [] (Object^ sender, EventArgs^ e) {
        // code here
    }
);

它会导致以下错误消息:

错误 C3364:“System::EventHandler”:委托构造函数的参数无效;委托目标需要是指向成员函数的指针

我是否在尝试不可能的事情,或者只是我的语法错误?

Is it possible to use the new lambda expressions in Visual C++ 2010 as CLR event handlers? I've tried the following code:

SomeEvent += gcnew EventHandler(
    [] (Object^ sender, EventArgs^ e) {
        // code here
    }
);

It results in the following error message:

error C3364: 'System::EventHandler' : invalid argument for delegate constructor; delegate target needs to be a pointer to a member function

Am I attempting the impossible, or is simply my syntax wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

江南月 2024-09-07 15:19:11

以下是我的解决方案,它允许将 lambda(以及任何函数对象 - 即可以调用 operator() 的任何对象)包装到委托中。它有一些限制 - 具体来说,它不支持带有跟踪引用参数的委托(C++/CLI 中的 %,C# 中的 ref/out );并且它对委托可以采用的参数数量有上限(因为 VC++2010 不支持可变参数模板) - 尽管可以轻松调整代码以支持您想要的数量。

#pragma once

#include <new>
#include <type_traits>

namespace detail
{
    struct return_type_helper
    {
    private:

        template<class D>
        struct dependent_false { enum { value = false }; };

        template <class D>
        struct illegal_delegate_type
        {
            static_assert(dependent_false<D>::value, "Delegates with more than 2 parameters, or with parameters of tracking reference types (T%), are not supported.");
        };

        struct anything
        {
            template<class T>
            operator T() const;
        };

    public:

        template<class D>
        static decltype(static_cast<D^>(nullptr)()) dummy(int(*)[1]);

        template<class D>
        static decltype(static_cast<D^>(nullptr)(anything())) dummy(int(*)[2]);

        template<class D>
        static decltype(static_cast<D^>(nullptr)(anything(), anything())) dummy(int(*)[3]);

        template <class D>
        static illegal_delegate_type<D> dummy(...);
    };


    template<class Func, class Aligner = char, bool Match = (std::tr1::alignment_of<Func>::value == std::tr1::alignment_of<Aligner>::value)>
    struct aligner
    {
        static_assert(Match, "Function object has unsupported alignment");
    };

    template<class Func, class Aligner>
    struct aligner<Func, Aligner, true>
    {
        typedef Aligner type;
    };

    template<class Func>
    struct aligner<Func, char, false> : aligner<Func, short>
    {
    };

    template<class Func>
    struct aligner<Func, short, false> : aligner<Func, int>
    {
    };

    template<class Func>
    struct aligner<Func, int, false> : aligner<Func, long>
    {
    };

    template<class Func>
    struct aligner<Func, long, false> : aligner<Func, long long>
    {
    };

    template<class Func>
    struct aligner<Func, long long, false> : aligner<Func, double>
    {
    };

    template<class Func>
    struct aligner<Func, double, false> : aligner<Func, void*>
    {
    };


    template<class F>
    ref class lambda_wrapper
    {
    public:

        lambda_wrapper(const F& f)
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            new(pf) F(f);
        }

        ~lambda_wrapper()
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            pf->~F();
        }

        template <class D>
        operator D^ ()
        {
            D^ d = nullptr;
            return gcnew D(this, &lambda_wrapper<F>::invoke<decltype(return_type_helper::dummy<D>(0))>);
        }

    private:

        template<class T>
        [System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential, Size = sizeof(T))]
        value struct embedded_storage
        {
        private:
            typename aligner<T>::type dummy;
        };


        embedded_storage<F> f_storage;

        template<class R>
        R invoke()
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            return (*pf)();
        }

        template<class R, class A1>
        R invoke(A1 a1)
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            return (*pf)(a1);
        }

        template<class R, class A1, class A2>
        R invoke(A1 a1, A2 a2)
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            return (*pf)(a1, a2);
        }
    };
}

template<class F>
detail::lambda_wrapper<F>^ make_delegate(F f)
{
    return gcnew detail::lambda_wrapper<F>(f);
}

示例用法:

Func<int, String^, int>^ f2 = make_delegate([&](int x, String^ y) -> int {
    Console::WriteLine("Func {0} {1}", x, y);
    return 2;
});

虽然这在技术上可以满足您的需求,但由于 C++0x lambda 被扩展为普通类,而不是 refvalue 的。由于普通类不能包含 C++/CLI 中的托管类型(即没有对象句柄类型的成员、没有跟踪引用类型的成员以及没有值类类型的成员),这意味着 lambda 无法捕获任何类型。这些类型的变量。据我所知,没有任何解决方法可以跟踪引用。对于值类,您可以获取指向它的非托管指针(pin_ptr 如果需要),并捕获它。

对于对象句柄,您可以将它们存储在 gcroot 中,并捕获它 - 但会产生严重的性能影响 - 在我的测试中,通过 gcroot 访问成员> 比使用普通对象句柄慢大约 40 倍。实际上,对于单次调用而言,它的绝对值并不大,但对于在循环中重复调用的东西(例如,大多数 LINQ 算法)来说,这将是一个杀手。但请注意,这仅适用于您需要捕获 lambda 中的句柄时!如果您只是用它来编写内联谓词或更新计数器,那么它就可以正常工作。

The following is my solution that allows one to wrap lambdas (as well as any function objects - i.e. anything on which operator() can be called) into delegates. It has some limits - specifically, it doesn't support delegates with tracking reference parameters (% in C++/CLI, ref/out in C#); and it has an upper limit on the number of parameters the delegate can take (because VC++2010 doesn't suppport vararg templates) - though the code can be trivially adjusted to support up to as many as you want.

#pragma once

#include <new>
#include <type_traits>

namespace detail
{
    struct return_type_helper
    {
    private:

        template<class D>
        struct dependent_false { enum { value = false }; };

        template <class D>
        struct illegal_delegate_type
        {
            static_assert(dependent_false<D>::value, "Delegates with more than 2 parameters, or with parameters of tracking reference types (T%), are not supported.");
        };

        struct anything
        {
            template<class T>
            operator T() const;
        };

    public:

        template<class D>
        static decltype(static_cast<D^>(nullptr)()) dummy(int(*)[1]);

        template<class D>
        static decltype(static_cast<D^>(nullptr)(anything())) dummy(int(*)[2]);

        template<class D>
        static decltype(static_cast<D^>(nullptr)(anything(), anything())) dummy(int(*)[3]);

        template <class D>
        static illegal_delegate_type<D> dummy(...);
    };


    template<class Func, class Aligner = char, bool Match = (std::tr1::alignment_of<Func>::value == std::tr1::alignment_of<Aligner>::value)>
    struct aligner
    {
        static_assert(Match, "Function object has unsupported alignment");
    };

    template<class Func, class Aligner>
    struct aligner<Func, Aligner, true>
    {
        typedef Aligner type;
    };

    template<class Func>
    struct aligner<Func, char, false> : aligner<Func, short>
    {
    };

    template<class Func>
    struct aligner<Func, short, false> : aligner<Func, int>
    {
    };

    template<class Func>
    struct aligner<Func, int, false> : aligner<Func, long>
    {
    };

    template<class Func>
    struct aligner<Func, long, false> : aligner<Func, long long>
    {
    };

    template<class Func>
    struct aligner<Func, long long, false> : aligner<Func, double>
    {
    };

    template<class Func>
    struct aligner<Func, double, false> : aligner<Func, void*>
    {
    };


    template<class F>
    ref class lambda_wrapper
    {
    public:

        lambda_wrapper(const F& f)
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            new(pf) F(f);
        }

        ~lambda_wrapper()
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            pf->~F();
        }

        template <class D>
        operator D^ ()
        {
            D^ d = nullptr;
            return gcnew D(this, &lambda_wrapper<F>::invoke<decltype(return_type_helper::dummy<D>(0))>);
        }

    private:

        template<class T>
        [System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential, Size = sizeof(T))]
        value struct embedded_storage
        {
        private:
            typename aligner<T>::type dummy;
        };


        embedded_storage<F> f_storage;

        template<class R>
        R invoke()
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            return (*pf)();
        }

        template<class R, class A1>
        R invoke(A1 a1)
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            return (*pf)(a1);
        }

        template<class R, class A1, class A2>
        R invoke(A1 a1, A2 a2)
        {
            pin_ptr<F> pf = (interior_ptr<F>)&f_storage;
            return (*pf)(a1, a2);
        }
    };
}

template<class F>
detail::lambda_wrapper<F>^ make_delegate(F f)
{
    return gcnew detail::lambda_wrapper<F>(f);
}

Sample usage:

Func<int, String^, int>^ f2 = make_delegate([&](int x, String^ y) -> int {
    Console::WriteLine("Func {0} {1}", x, y);
    return 2;
});

While this technically does what you want, the practical applications are somewhat limited due to the fact that C++0x lambdas are expanded into plain classes, not ref or value ones. Since plain classes cannot contain managed types in C++/CLI (i.e. no members of object handle type, no members of tracking reference type, and no members of value class type), this means that lambdas cannot capture any variables of those types, either. There is no workaround I'm aware of for tracking references. For value class, you can take an unmanaged pointer to it (pin_ptr if needed), and capture that.

For object handles, you can store them in gcroot<T>, and capture that - but there are severe performance implications - in my tests, accessing a member via gcroot<T> is about 40x times slower than doing it using a plain object handle. It's actually not much in absolute measure for a single call, but for something that is called repeatedly in a loop - say, most LINQ algorithms - it would be a killer. But note that this only applies when you need to capture a handle in the lambda! If you just use it to write a predicate inline, or to update a counter, it'll work just fine.

清旖 2024-09-07 15:19:11

没办法,C++/CLI 编译器没有更新以接受 lambda 语法。顺便说一句,考虑到托管代码的领先优势,这相当讽刺。

No can do, the C++/CLI compiler didn't get updated to accept the lambda syntax. Fairly ironic btw given the head-start that managed code had.

天暗了我发光 2024-09-07 15:19:11

此页面有一些 C++ 的 lambda 示例:

http://msdn.microsoft.com/en-us/library/dd293608%28v=VS.100%29.aspx

Microsoft VS2010 C++ 改进看起来确实实现了 C++0x lambda 规范。因此,它们完全是非托管的,并且属于 lambda 类型。

Microsoft 文档中没有任何内容暗示可以将 C++ lambda 用作 CLR lambda。在这个阶段,我不得不说,您不能使用 C++ lambda 作为托管委托的处理程序。

This page has a few examples of lambdas for C++:

http://msdn.microsoft.com/en-us/library/dd293608%28v=VS.100%29.aspx

Microsoft VS2010 C++ improvements look like they actually implement C++0x lambda spec. As such they are purely unmanaged and are of type lambda.

There is nothing in the Microsoft documentation that hints at possibility of using C++ lambdas as CLR lambdas. At this stage I have to say that you cannot use C++ lambdas as handlers for managed delegates.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文