locked
C++/CX lamda and generic event handler

    Question

  • Hi C++/CX experts,

    Here's seeking expertise on C++/CX lambda, trying to propagate events using a generic delegate.

    This works fine when the constructor is not overloaded:

    Generator<StartEventArgs^>^ generator = ref new Generator<StartEventArgs^>( [] ()
    {
        return ref new StartEventArgs();
    }

    );

    This fails when the constructor is overloaded:

    Generator<AuthEventArgs^>^ generator =  ref new Generator<AuthEventArgs^>([&] ()
    	{
    		ref new AuthEventArgs(token);
    	}
    );

    Here's the error:

    Warning 1 warning C4570: 'Generator' : is not explicitly declared as abstract but has abstract functions C:\Users\rsomaskandan\Documents\Visual Studio 2012\Projects\CxxWinRTTemplate1\CxxWinRTTemplate1\RefTemplate.cpp 10 1 CxxWinRTTemplate1
    Error 2 error C2440: 'return' : cannot convert from 'void' to 'AuthEventArgs ^' C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\vccorlib.h 850 1 CxxWinRTTemplate1

    Here's the complete code snippet:

    // RefTemplate.cpp : Defines the entry point for the console application.
    //
    
    #include <iostream>
    using namespace std;
    using namespace Platform;
    using namespace Windows::Foundation;
    
    generic <class T>
    delegate T Generator();
    
    		enum class ErrorType
    		{
    			UNKNOWN = 0,
    			HTTP = 1,
    			API = 2,
    			NETWORK = 3
    		};
    
    		ref class StartEventArgs sealed
    		{
    
    		};
    
    		ref class AuthEventArgs sealed
    		{		
    			private:
    				String^ authEventArgsToken;
    
    			public:
    				AuthEventArgs(String^ token)
    				{
    					Token = token;
    				}
    				property String^ Token
    				{
    					String^ get()
    					{
    						return authEventArgsToken;
    					}
    					void set(String^ value)
    					{
    						authEventArgsToken = value;
    					}
    				}
    		};
    
    		ref class CancelEventArgs sealed
    		{		
    			private:
    				String^ cancelEventArgsToken;
    
    			public:
    				CancelEventArgs(String^ token)
    				{
    					Token = token;
    				}
    				property String^ Token
    				{
    					String^ get()
    					{
    						return cancelEventArgsToken;
    					}
    					void set(String^ value)
    					{
    						cancelEventArgsToken = value;
    					}
    				}
    		};
    
    		ref class ErrorEventArgs sealed
    		{		
    			private:
    				ErrorType errorEventArgsType;
    				String^ errorEventArgsCode;
    				String^ errorEventArgsMessage;
    				String^ errorEventArgsDetail;
    
    			public:
    				ErrorEventArgs(String^ name, String^ message, String^ detail)
    				{					
    					 Code = name;
    					 Message = message;
    					 Detail = detail;
    				}
    
    				property String^ Code
    				{
    					String^ get()
    					{
    						return errorEventArgsCode;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsCode = value;
    					}
    				}
    
    				property String^ Message
    				{
    					String^ get()
    					{
    						return errorEventArgsMessage;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsMessage = value;
    					}
    				}
    
    				property String^ Detail
    				{
    					String^ get()
    					{
    						return errorEventArgsDetail;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsDetail = value;
    					}
    				}
    		};
    
    		ref class CompleteEventArgs sealed
    		{		
    			private:
    				String^ errorEventArgsToken;
    				String^ errorEventArgsTransactionID;
    				String^ errorEventArgsPayerID;
    
    			public:
    				CompleteEventArgs(String^ token, String^ transactionID, String^ payerID)
    				{
    					 Token = token;
    					 TransactionID = transactionID;
    					 PayerID = payerID;
    				}
    				property String^ Token
    				{
    					String^ get()
    					{
    						return errorEventArgsToken;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsToken = value;
    					}
    				}
    
    				property String^ TransactionID
    				{
    					String^ get()
    					{
    						return errorEventArgsTransactionID;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsTransactionID = value;
    					}
    				}
    
    				property String^ PayerID
    				{
    					String^ get()
    					{
    						return errorEventArgsPayerID;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsPayerID = value;
    					}
    				}				
    		};
    
    ref class BuyNow sealed
    {
    	internal:
    	event EventHandler<StartEventArgs^>^ Start;
    	void OnStart();	 
    	void OnAuth(String^ token);
    	void OnCancel(String^ token);
    	void OnError(String^ name, String^message);
    	void OnError(String^ name, String^ message, String^ detail);
    	void OnComplete(String^ token, String^ transactionId, String^payerId);
    };
    
    void BuyNow::OnStart()
    {
    	Generator<StartEventArgs^>^ generator = ref new Generator<StartEventArgs^>( [] ()
        {
            return ref new StartEventArgs();
        });
    }
    
    void BuyNow::OnAuth(String^ token)
    {
    Generator<AuthEventArgs^>^ generator = 
    	ref new Generator<AuthEventArgs^>
    	(
    	[&] ()
    		{
    													
    				ref new AuthEventArgs(token);
    		}
    	);
    }
    
    void BuyNow::OnCancel(String^ token)
    {
    	Generator<CancelEventArgs^>^ generator = ref new Generator<CancelEventArgs^>
    											(
    											[&] ()
    												{
    													
    													 ref new CancelEventArgs(token);
    												}
    											);
    }
    
    void BuyNow::OnError(String^ name, String^message)
    {					
    	OnError(name, message, nullptr);
    }
    
    void BuyNow::OnError(String^ name, String^ message, String^ detail)
    {
    	Generator<ErrorEventArgs^>^ generator = ref new Generator<ErrorEventArgs^>
    											(
    											[&] ()
    												{
    													
    													  ref new ErrorEventArgs(name, message, detail);
    												}
    											);		
    	//Emit<ErrorEventArgs>(generator);
    }
    
    void BuyNow::OnComplete(String^ token, String^ transactionId, String^payerId)
    {
    	Generator<CompleteEventArgs^>^ generator = ref new Generator<CompleteEventArgs^>
    											(
    											[&] ()
    												{
    													
    													 ref new CompleteEventArgs(token, transactionId, payerId);
    												}
    											);
    
    	//EmitAndComplete<CompleteEventArgs>(generator);
    }
    
    
    int main(Platform::Array<Platform::String^>^ args)
    {	
    	BuyNow^ bn = ref new BuyNow();
    	bn->OnStart();	
    	std::cin.get(); 
    	return 0;
    }

    I am a .NET developer pitching into C++/CX (and learning). Please help with Lamda expression syntax/code snippet illustration in C++/CX as MSDN documentation didn't help.

    Thanks


    • Edited by recherche Monday, May 27, 2013 6:54 AM typo
    Monday, May 27, 2013 6:53 AM

Answers

  • I am new to C++ also and Generics, Templates and Delegates are quite confusing to me--especially when used in combination!

    The error is due to the mismatch of the generic delegate declaration which calls for a return value of type T.

    All of the lambda expressions except for the one for StartEventArgs, do not return a value (i.e. void).

    Either change the definition to 

    generic <class T> delegate void Generator();

    (and remove the return from the StartEventArgs usage) or

    add the return to the lambdas as in:

    void BuyNow::OnAuth(String^ token)
    {
    	auto generator = ref new Generator<AuthEventArgs^>( [&] ()		
    	{
    		return ref new AuthEventArgs(token);
    	});
    }

    As to which form you need for your scenario, I don't know.


    • Edited by jrboddie Monday, May 27, 2013 1:43 PM
    • Marked as answer by recherche Monday, May 27, 2013 6:01 PM
    Monday, May 27, 2013 1:42 PM

All replies

  • I am new to C++ also and Generics, Templates and Delegates are quite confusing to me--especially when used in combination!

    The error is due to the mismatch of the generic delegate declaration which calls for a return value of type T.

    All of the lambda expressions except for the one for StartEventArgs, do not return a value (i.e. void).

    Either change the definition to 

    generic <class T> delegate void Generator();

    (and remove the return from the StartEventArgs usage) or

    add the return to the lambdas as in:

    void BuyNow::OnAuth(String^ token)
    {
    	auto generator = ref new Generator<AuthEventArgs^>( [&] ()		
    	{
    		return ref new AuthEventArgs(token);
    	});
    }

    As to which form you need for your scenario, I don't know.


    • Edited by jrboddie Monday, May 27, 2013 1:43 PM
    • Marked as answer by recherche Monday, May 27, 2013 6:01 PM
    Monday, May 27, 2013 1:42 PM
  •  

    void BuyNow::OnAuth(String^ token)
    {
    	auto generator = ref new Generator<AuthEventArgs^>( [&] ()		
    	{
    		return ref new AuthEventArgs(token);
    	});
    }

     

    Great, jrboddie. You hit the nail on its head. Thanks a lot.

    Monday, May 27, 2013 6:00 PM
  •  

    void BuyNow::OnAuth(String^ token)
    {
    	auto generator = ref new Generator<AuthEventArgs^>( [&] ()		
    	{
    		return ref new AuthEventArgs(token);
    	});
    }

    Hi jrboddie,

    How to interrogate the type of a generic delegate at runtime, and emit the events registered for that generic delegate?

    Here's the C# code snippet:

            private void Emit<T>(Generator<T> generator)
            {
                TypeInfo info = this.GetType().GetTypeInfo();
                TypeInfo genericType = typeof(T).GetTypeInfo();
    
                // Get all the available events
                foreach (EventInfo eventInfo in info.DeclaredEvents)
                {
                    // Get the type arguments for the current event (really, there should only be one)
                    Type[] typeParameters = eventInfo.EventHandlerType.GetTypeInfo().GenericTypeArguments;
                    foreach (Type parameterType in typeParameters)
                    {
                        // If the type parameter matches the type we're trying to emit...
                        if (parameterType.GetTypeInfo().IsAssignableFrom(genericType))
                        {
                            // Get the field of the same name so we can access the token table
                            FieldInfo eventField = info.GetDeclaredField(eventInfo.Name);
                            EventRegistrationTokenTable<EventHandler<T>> tokenTable = eventField.GetValue(this) as EventRegistrationTokenTable<EventHandler<T>>;
                            if (tokenTable != null)
                            {
                                // Events are registered, so generate our event args, invoke, and break out
                                EventHandler<T> handler = tokenTable.InvocationList;
                                T eventArgs = generator();
                                handler.DynamicInvoke(new object[] { this, eventArgs });
                                goto complete;
                            }
                        }
                    }
                }
                complete: { }
            }
    
            private void EmitAndComplete<T>(Generator<T> generator)
            {
                Debug.Assert(_complete, "Completion events (Complete, Error, Cancel) should only emit once.");
                if (!_complete)
                {
                    Emit<T>(generator);
                    _complete = true;
                }
            }

    How to interrogate the type at runtime in C++/CX?

    MSDN http://msdn.microsoft.com/en-us/library/windows/apps/hh699869.aspx does not talk about EventInfo?

    template <class T>
    void BuyNow::Emit(Generator<T>^ generator)
    {
        Platform::Object^ instance = this->GetType();       
        // . . .
    }

    Here the entire C++ code snippet of generic delegate:

    // RefTemplate.cpp : Defines the entry point for the console application.
    //
    
    #include <iostream>
    using namespace std;
    using namespace Platform;
    using namespace Windows::Foundation;
    
    generic <class T>
    delegate T Generator();
    
    		enum class ErrorType
    		{
    			UNKNOWN = 0,
    			HTTP = 1,
    			API = 2,
    			NETWORK = 3
    		};
    
    		ref class StartEventArgs sealed
    		{
    
    		};
    
    		ref class AuthEventArgs sealed
    		{		
    			private:
    				String^ authEventArgsToken;
    
    			public:
    				AuthEventArgs(String^ token)
    				{
    					Token = token;
    				}
    				property String^ Token
    				{
    					String^ get()
    					{
    						return authEventArgsToken;
    					}
    					void set(String^ value)
    					{
    						authEventArgsToken = value;
    					}
    				}
    		};
    
    		ref class CancelEventArgs sealed
    		{		
    			private:
    				String^ cancelEventArgsToken;
    
    			public:
    				CancelEventArgs(String^ token)
    				{
    					Token = token;
    				}
    				property String^ Token
    				{
    					String^ get()
    					{
    						return cancelEventArgsToken;
    					}
    					void set(String^ value)
    					{
    						cancelEventArgsToken = value;
    					}
    				}
    		};
    
    		ref class ErrorEventArgs sealed
    		{		
    			private:
    				ErrorType errorEventArgsType;
    				String^ errorEventArgsCode;
    				String^ errorEventArgsMessage;
    				String^ errorEventArgsDetail;
    
    			public:
    				ErrorEventArgs(String^ name, String^ message, String^ detail)
    				{					
    					 Code = name;
    					 Message = message;
    					 Detail = detail;
    				}
    
    				property String^ Code
    				{
    					String^ get()
    					{
    						return errorEventArgsCode;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsCode = value;
    					}
    				}
    
    				property String^ Message
    				{
    					String^ get()
    					{
    						return errorEventArgsMessage;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsMessage = value;
    					}
    				}
    
    				property String^ Detail
    				{
    					String^ get()
    					{
    						return errorEventArgsDetail;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsDetail = value;
    					}
    				}
    		};
    
    		ref class CompleteEventArgs sealed
    		{		
    			private:
    				String^ errorEventArgsToken;
    				String^ errorEventArgsTransactionID;
    				String^ errorEventArgsPayerID;
    
    			public:
    				CompleteEventArgs(String^ token, String^ transactionID, String^ payerID)
    				{
    					 Token = token;
    					 TransactionID = transactionID;
    					 PayerID = payerID;
    				}
    				property String^ Token
    				{
    					String^ get()
    					{
    						return errorEventArgsToken;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsToken = value;
    					}
    				}
    
    				property String^ TransactionID
    				{
    					String^ get()
    					{
    						return errorEventArgsTransactionID;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsTransactionID = value;
    					}
    				}
    
    				property String^ PayerID
    				{
    					String^ get()
    					{
    						return errorEventArgsPayerID;
    					}
    					void set(String^ value)
    					{
    						errorEventArgsPayerID = value;
    					}
    				}				
    		};
    
    ref class BuyNow sealed
    {
    	internal:
    	event EventHandler<StartEventArgs^>^ Start;
    	void OnStart();	 
    	void OnAuth(String^ token);
    	void OnCancel(String^ token);
    	void OnError(String^ name, String^message);
    	void OnError(String^ name, String^ message, String^ detail);
    	void OnComplete(String^ token, String^ transactionId, String^payerId);
    
    	template <class T>
    	void Emit(Generator<T>^ generator);
    };
    
    
    void BuyNow::OnStart()
    {
    	Generator<StartEventArgs^>^ generator = ref new Generator<StartEventArgs^>([] ()
    		{
    			return ref new StartEventArgs();
    		}
    	);
    
    	Emit(generator);
    }
    
    void BuyNow::OnAuth(String^ token)
    {
    	Generator<AuthEventArgs^>^ generator = ref new Generator<AuthEventArgs^>( [&] ()		
    		{
    			return ref new AuthEventArgs(token);
    		}
    	);
    	 Emit(generator);
    }
    
    void BuyNow::OnCancel(String^ token)
    {
    	Generator<CancelEventArgs^>^ generator = ref new Generator<CancelEventArgs^>( [&] ()		
    		{
    			return ref new CancelEventArgs(token);
    		}
    	);
    	 Emit(generator);
    }
    
    void BuyNow::OnError(String^ name, String^message)
    {					
    	OnError(name, message, nullptr);
    }
    
    void BuyNow::OnError(String^ name, String^ message, String^ detail)
    {
    	Generator<ErrorEventArgs^>^ generator = ref new Generator<ErrorEventArgs^>( [&] ()		
    		{
    			return ref new ErrorEventArgs(name, message, detail);
    		}
    	);		
    	//Emit<ErrorEventArgs>(generator);
    }
    
    void BuyNow::OnComplete(String^ token, String^ transactionID, String^ payerID)
    {
    	Generator<CompleteEventArgs^>^ generator = ref new Generator<CompleteEventArgs^>( [&] ()		
    		{
    			return ref new CompleteEventArgs(token, transactionID, payerID);
    		}
    	);					
    	//EmitAndComplete<CompleteEventArgs>(generator);
    }
    
    template <class T>
    void BuyNow::Emit(Generator<T>^ generator)
    {
    		Platform::Object^ instance = this->GetType();
            
    		// . . .
    }
    
    int main(Platform::Array<Platform::String^>^ args)
    {	
    	BuyNow^ bn = ref new BuyNow();
    	bn->OnStart();	
    	std::cin.get(); 
    	return 0;
    }

    Thanks



    • Edited by recherche Tuesday, May 28, 2013 1:05 PM typo
    Monday, May 27, 2013 6:35 PM