From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,FORGED_GMAIL_RCVD, FREEMAIL_FROM autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,ec06888ca495a8ff X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news2.google.com!postnews.google.com!v33g2000cwv.googlegroups.com!not-for-mail From: "benibilme@gmail.com" Newsgroups: comp.lang.ada Subject: Re: Newbie question: Implementing a callback mechanism with Ada 83 Date: 11 Feb 2007 13:40:53 -0800 Organization: http://groups.google.com Message-ID: <1171230053.064136.26460@v33g2000cwv.googlegroups.com> References: <1171136299.920852.179650@v33g2000cwv.googlegroups.com> NNTP-Posting-Host: 85.100.242.68 Mime-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" X-Trace: posting.google.com 1171230058 19856 127.0.0.1 (11 Feb 2007 21:40:58 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: Sun, 11 Feb 2007 21:40:58 +0000 (UTC) In-Reply-To: User-Agent: G2/1.0 X-HTTP-UserAgent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1,gzip(gfe),gzip(gfe) Complaints-To: groups-abuse@google.com Injection-Info: v33g2000cwv.googlegroups.com; posting-host=85.100.242.68; posting-account=nCJDeg0AAABbwCDK7NEKPtAEyr16KPS9 Xref: g2news2.google.com comp.lang.ada:9266 Date: 2007-02-11T13:40:53-08:00 List-Id: First of all, thank you all for the responses. What I wanted to achieve is to decouple the packages which are responsible for the handling of specific events. The initialization of each specific package would register for the events that it cares about and only become dependent to the event manager package. Event manager package would provide the interface for registering callbacks and the static data for the events, firing an event and removing a registration etc. A package that monitors a state or an event fires a defined event in the event manager package with the dynamic data and event manager module would call successively the registered callbacks in the different packages which are interested in the event. That was my intention. As a matter of fact that is a classic solution for event driven programs that I often use when I code with C. It adds one level of indirection and breaks the explicit coupling between the event generators and handlers. It is just a simple publish and subscribe paradigm. I present a very quick implementation of this module in C below. I wanted to implement this module in Ada 83 which I am failed to do. #include #include typedef enum { ev_start, ev_stop, ev_error, ev_LAST } Event_Id; typedef void (Event_Handler) (Event_Id id, void *event_data, void *user_data); typedef struct { Event_Handler *handler; void *user_data; } Event_Handler_Info; typedef struct { short nr_of_handlers; Event_Handler_Info *handler_info; } Event_Binding; static boolean active; static Event_Binding events [ev_LAST]; void Event_Handler_Create (void) { Event_Id id; Event_Binding *binding; binding = &events [0]; for (id = 0, binding = events; id < ev_LAST; id++, binding++) { binding -> nr_of_handlers = 0; } active = false; } void Register_For_Event (Event_Id id, void *user_data, Event_Handler handler) { Event_Handler_Info *p, *q, *r; int i; if (handler != NULL) { Remove_Event_Handler (id, handler); p = (Event_Handler_Info *) malloc ((events [id].nr_of_handlers + 1) * sizeof (Event_Handler_Info)); r = q = events [id].handler_info; events [id].handler_info = p; for (i = 0; i < events [id].nr_of_handlers; i++) { *p++ = *q++; } p -> handler = handler; p -> user_data = user_data; events [id].nr_of_handlers++; if (r != NULL) { free (r); } } else { } } /* end of Install_Event_Handler */ void Deregister_For_Event (Event_Id id, Event_Handler handler) { Event_Handler_Info *handler_info; int nr_of_handlers; int i; boolean found; if (handler != NULL) { i = 0; found = false; nr_of_handlers = events [id].nr_of_handlers; handler_info = events [id].handler_info; while (i < nr_of_handlers && !found) { if (handler_info -> handler == handler) { found = true; } else { handler_info++; i++; } } if (found) { handler_info -> handler = events [id].handler_info [nr_of_handlers].handler; handler_info -> user_data = events [id].handler_info [nr_of_handlers].user_data; events [id].nr_of_handlers--; } } else { } } void Fire_Event (Event_Id id, void *event_data) { Event_Handler_Info *handler_info; int nr_of_handlers; int i; nr_of_handlers = events [id].nr_of_handlers; handler_info = events [id].handler_info; for (i = 0; i < nr_of_handlers; i++) { (handler_info -> handler) (id, event_data, handler_info -> user_data); handler_info++; } } /* end of Post_Event */ Of course I can solve my problem without this kind of capability. It will be brute force solution and according to me an ugly solution. For example when an event occurs, the module that detected the event has to call the subprograms in different packages that are interested in the event. The packages will be tightly coupled to each other. The package that triggers the event has to know the packages and the subprograms that interested in the event. I can see the Ada 83 reasoning. Yes, the code will be more readable maybe and statically provable but very very verbose and inflexible. At least with my little Ada knowledge, that is what I can foresee right now. The code has to be single threaded. It is a requirement. By the way, a friend of mine whose is more experienced in Ada send me this mail which I wanted to share with you. =========================================================== "I can't claim to be an expert at Ada programming, and I usually use Ada95, so I may suggest a solution that is cannot be implemented in Ada83, but I have a few suggestions that you could try. 1. The solution to having no access-to-subprogram type in Ada83 is usually passing the address of the function around and then in Ada a function can be declared to 'use' that address as it's own like so: procedure Foo; procedure Install_Handler ( Event : Event_Type; Event_Handler : System.Address ) is begin Event_Array(Event_Type) := Event_Handler; end Install_Handler; procedure Call_Handler ( Event : Event_Type ) is procedure Temp; for Temp use at Event_Array(Event); begin Temp; end Call_Handler; -- using the procedure Install_Handler(Event_One, Foo'Address); 2. It is not unusual with an Ada program for the low level OS routines to be implemented in C/C++ or Assembly. Then you could import the C functions (such as the ones you attached). This is not due to Ada not being able to implement low-level routines, it is usually used when you have existing code, or an OS, that works well and you are used to using it. The first step would be to define the procedures in Ada, and then declare them to be imported from C. Then it is necessary to define the types used in the imported subprograms with the correct data size (8 bits, 16 bits, etc.), and then the correct values (for the enumerations) so that the data passed from the Ada code matches the C code exactly. This last step is very important because by default most Ada compilers will try to use the most efficient representation for data types as possible, and this will usually cause problems when moving data between Ada and another language. The "Interfaces" standard library has useful C-specific data types, although I cannot remember if that package was new in Ada95 or not. The solution in this case for passing the procedure address around is to use a generic 32-bit value instead of the System.Address type, and then when calling the C functions cast the System'Address value of the procedure (that is gathered from the Function'Address attribute), into the 32-bit value using a generic standard function called Unchecked_Conversion; -- The procedures in Ada procedure Event_Handler_Create; procedure Post_Event ( ID : Event_Data; Interfaces.Unsigned_32 ); procedure Install_Event_Handler ( ID : Event_ID; User_Data : Interfaces.Unsigned_32 ; Handler : Interfaces.Unsigned_32 ); procedure Remove_Event_Handler ( ID : Event_ID; Handler : Interfaces.Unsigned_32 ); -- The importation of the procedures from C -- C is the language the function is declared in, the second paramter is the Ada procedure name, and the third -- parameter is what the name of the C function is. pragma Import(C, Event_Handler_Create, "Event_Handler_Create"); pragma Import(C, Post_Event, "Post_Event"); pragma Import(C, Install_Event_Handler, "Install_Event_Handler"); pragma Import(C, Remove_Event_Handler, "Remove_Event_Handler"); type Event_ID is ( ev_start, ev_stop, ev_error, ev_LAST ); for Event_ID use ( ev_start => 0, ev_stop => 1, ev_error => 2, ev_LAST => 3); for Event_ID'Size use 8; -- Global conversion functions with System; with Unchecked_Conversion; function To_Unsigned_32 is new Unchecked_Conversion ( System.Address, Interfaces.Unsigned_32 ); function To_Address is new Unchecked_Conversion ( Interfaces.Unsigned_32, System.Address ); ... -- event specific data procedure My_Handler ( ID : Event_ID; Event_Data : Interfaces.Unsigned_32; User_Data : Interfaces.Unsigned_32 ) is Data : Integer; for Data use at To_Address(Event_Data); begin -- do stuff end My_Handler; Data : Integer := 5; ... -- installing the event Install_Event_Handler ( ID => ev_start, User_Data => To_Unsigned_32(Data'Address), Handler => To_Unsigned_32(My_Handler'Address) ); Hopefully between those examples you can figure out what you would like to do. Essentially, when you are working with low-level code the use of the Unchecked_Conversion generic function, and the System.Address type are the biggest tools to remember. And when interfacing with other lanugages the following representation clauses are your friend: for X'Size use ... for X use ( A => 1...) for X use at ... -- In Ada95 this would be "for X'Address use ..." I hope that helps!" ===============================================================================