Lambda expressions as CLR (.NET) delegates / event handlers in Visual C++ 2010

后端 未结 3 535
伪装坚强ぢ
伪装坚强ぢ 2020-12-11 02:21

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(
          


        
3条回答
  •  醉酒成梦
    2020-12-11 03:17

    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 
    #include 
    
    namespace detail
    {
        struct return_type_helper
        {
        private:
    
            template
            struct dependent_false { enum { value = false }; };
    
            template 
            struct illegal_delegate_type
            {
                static_assert(dependent_false::value, "Delegates with more than 2 parameters, or with parameters of tracking reference types (T%), are not supported.");
            };
    
            struct anything
            {
                template
                operator T() const;
            };
    
        public:
    
            template
            static decltype(static_cast(nullptr)()) dummy(int(*)[1]);
    
            template
            static decltype(static_cast(nullptr)(anything())) dummy(int(*)[2]);
    
            template
            static decltype(static_cast(nullptr)(anything(), anything())) dummy(int(*)[3]);
    
            template 
            static illegal_delegate_type dummy(...);
        };
    
    
        template::value == std::tr1::alignment_of::value)>
        struct aligner
        {
            static_assert(Match, "Function object has unsupported alignment");
        };
    
        template
        struct aligner
        {
            typedef Aligner type;
        };
    
        template
        struct aligner : aligner
        {
        };
    
        template
        struct aligner : aligner
        {
        };
    
        template
        struct aligner : aligner
        {
        };
    
        template
        struct aligner : aligner
        {
        };
    
        template
        struct aligner : aligner
        {
        };
    
        template
        struct aligner : aligner
        {
        };
    
    
        template
        ref class lambda_wrapper
        {
        public:
    
            lambda_wrapper(const F& f)
            {
                pin_ptr pf = (interior_ptr)&f_storage;
                new(pf) F(f);
            }
    
            ~lambda_wrapper()
            {
                pin_ptr pf = (interior_ptr)&f_storage;
                pf->~F();
            }
    
            template 
            operator D^ ()
            {
                D^ d = nullptr;
                return gcnew D(this, &lambda_wrapper::invoke(0))>);
            }
    
        private:
    
            template
            [System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential, Size = sizeof(T))]
            value struct embedded_storage
            {
            private:
                typename aligner::type dummy;
            };
    
    
            embedded_storage f_storage;
    
            template
            R invoke()
            {
                pin_ptr pf = (interior_ptr)&f_storage;
                return (*pf)();
            }
    
            template
            R invoke(A1 a1)
            {
                pin_ptr pf = (interior_ptr)&f_storage;
                return (*pf)(a1);
            }
    
            template
            R invoke(A1 a1, A2 a2)
            {
                pin_ptr pf = (interior_ptr)&f_storage;
                return (*pf)(a1, a2);
            }
        };
    }
    
    template
    detail::lambda_wrapper^ make_delegate(F f)
    {
        return gcnew detail::lambda_wrapper(f);
    }
    

    Sample usage:

    Func^ 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, and capture that - but there are severe performance implications - in my tests, accessing a member via gcroot 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.

提交回复
热议问题