Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way of raising events from C++/CLI?

I was wondering what's the proper way of raising events from C++/CLI. In C# one should first make a copy of the handler, check if it's not null, and then call it. Is there a similar practice for C++/CLI?

like image 673
Filip Frącz Avatar asked Jan 20 '09 19:01

Filip Frącz


People also ask

How do you raise an event?

Typically, to raise an event, you add a method that is marked as protected and virtual (in C#) or Protected and Overridable (in Visual Basic). Name this method On EventName; for example, OnDataReceived .

How do you handle events in C++?

In native C++ event handling, you set up an event source and event receiver using the event_source and event_receiver attributes, respectively, specifying type = native . These attributes allow the classes they're applied on to fire events and handle events in a native, non-COM context.

What does event in C mean?

An event is an association between a delegate and an event handler. An event handler is a member function that responds when the event gets triggered. It allows clients from any class to register methods that match the signature and return type of the underlying delegate.

What are C hash events?

C # in Telugu Events are user actions such as key press, clicks, mouse movements, etc., or some occurrence such as system generated notifications. Applications need to respond to events when they occur. For example, interrupts. Events are used for inter-process communication.


3 Answers

This isn't the whole story! You don't usually have to worry about null event handlers in C++/CLI. The code for these checks is generated for you. Consider the following trivial C++/CLI class.

public ref class MyClass
{
public:
    event System::EventHandler ^ MyEvent;
};

If you compile this class, and disassemble it using Reflector, you get the following c# code.

public class MyClass
{
    // Fields
    private EventHandler <backing_store>MyEvent;

    // Events
    public event EventHandler MyEvent
    {
        [MethodImpl(MethodImplOptions.Synchronized)] add
        {
            this.<backing_store>MyEvent = (EventHandler) Delegate.Combine(this.<backing_store>MyEvent, value);
        }
        [MethodImpl(MethodImplOptions.Synchronized)] remove
        {
            this.<backing_store>MyEvent = (EventHandler) Delegate.Remove(this.<backing_store>MyEvent, value);
        }
        raise
        {
            EventHandler <tmp> = null;
            <tmp> = this.<backing_store>MyEvent;
            if (<tmp> != null)
            {
                <tmp>(value0, value1);
            }
        }
    }
}

The usual checks are being done in the raise method. Unless you really want custom behavior, you should feel comfortable declaring your event as in the above class, and raising it without fear of a null handler.

like image 176
IV. Avatar answered Sep 19 '22 11:09

IV.


C++/CLI allows you to override raise in custom event handlers so you don't have to test for null or copy when raising the event. Of course, inside your custom raise you still have to do this.

Example, adapted from the MSDN for correctness:

public delegate void f(int);

public ref struct E {
   f ^ _E;
public:
   void handler(int i) {
      System::Console::WriteLine(i);
   }

   E() {
      _E = nullptr;
   }

   event f^ Event {
      void add(f ^ d) {
         _E += d;
      }
      void remove(f ^ d) {
        _E -= d;
      }
      void raise(int i) {
         f^ tmp = _E;
         if (tmp) {
            tmp->Invoke(i);
         }
      }
   }

   static void Go() {
      E^ pE = gcnew E;
      pE->Event += gcnew f(pE, &E::handler);
      pE->Event(17);
   }
};

int main() {
   E::Go();
}
like image 42
Konrad Rudolph Avatar answered Sep 20 '22 11:09

Konrad Rudolph


If your issue is that raise isn't private, then explicitly implement it like the docs say:

http://msdn.microsoft.com/en-us/library/5f3csfsa.aspx

In summary:

If you just use the event keyword, you create a "trivial" event. The compiler generates add/remove/raise and the delegate member for you. The generated raise function (as the docs say) checks for nullptr. Trivial events are documented here:

http://msdn.microsoft.com/en-us/library/4b612y2s.aspx

If you want "more control", for example to make raise private, then you have to explicitly implement the members as shown in the link. You must explicitly declare a data member for the delegate type. Then you use the event keyword to declare the event-related members, as in the Microsoft example:

// event keyword introduces the scope wherein I'm defining the required methods
// "f" is my delegate type
// "Event" is the unrealistic name of the event itself
event f^ Event
{
      // add is public (because the event block is public)
      // "_E" is the private delegate data member of type "f"
      void add(f ^ d) { _E += d; }

   // making remove private
   private:
      void remove(f ^ d) { _E -= d; }

   // making raise protected
   protected:
      void raise(int i)
      { 
         // check for nullptr
         if (_E)
         {
            _E->Invoke(i);
         }
      }
}// end event block

Wordy, but there it is.

-reilly.

like image 24
Reilly Avatar answered Sep 18 '22 11:09

Reilly