* Newbie question: Implementing a callback mechanism with Ada 83 @ 2007-02-10 19:38 benibilme 2007-02-10 20:40 ` Dmitry A. Kazakov 2007-02-11 2:46 ` Jeffrey R. Carter 0 siblings, 2 replies; 6+ messages in thread From: benibilme @ 2007-02-10 19:38 UTC (permalink / raw) Hello, I need to develop an Ada application which must conform to Ada83. It will be part of legacy system. I am very much new in Ada. The application I have to develop is very much event driven. As an old time C programmer I wanted to implement an event handling module which you can subscribe callback functions and trigger the events when necessary and unfortunately I have realized that Ada 83 does not support pointer to subprograms. I have read about generics with which defining subprograms as parameters of subprograms seems possible. On the other hand, I could not figure out how to do it. I simply want to store function pointers to some data structure for some certain events and call them with dynamic data when the event is triggered. How can I do it in Ada 83 without the support of pointers to subprograms. It is clear how to do this Ada95 as there is a direct support for pointer to subprograms. Any help is much appreciated. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Newbie question: Implementing a callback mechanism with Ada 83 2007-02-10 19:38 Newbie question: Implementing a callback mechanism with Ada 83 benibilme @ 2007-02-10 20:40 ` Dmitry A. Kazakov 2007-02-11 2:46 ` Jeffrey R. Carter 1 sibling, 0 replies; 6+ messages in thread From: Dmitry A. Kazakov @ 2007-02-10 20:40 UTC (permalink / raw) On 10 Feb 2007 11:38:19 -0800, benibilme@gmail.com wrote: > I need to develop an Ada application which must conform to Ada83. It > will be part of legacy system. I am very much new in Ada. The > application I have to develop is very much event driven. As an old > time C programmer I wanted to implement an event handling module which > you can subscribe callback functions and trigger the events when > necessary and unfortunately I have realized that Ada 83 does not > support pointer to subprograms. I have read about generics with which > defining subprograms as parameters of subprograms seems possible. On > the other hand, I could not figure out how to do it. > > I simply want to store function pointers to some data structure for > some certain events and call them with dynamic data when the event is > triggered. > > How can I do it in Ada 83 without the support of pointers to > subprograms. It is clear how to do this Ada95 as there is a direct > support for pointer to subprograms. > > Any help is much appreciated. I remember that once I used access-to-task to emulate access-to-subprogram. You can make handlers tasks and serve events in rendezvous. Another [tedious] possibility could be a proxy subprogram: type Call_Back_Id is (Foo_Id, Bar_Id, ...); procedure Call_Back (Id : Call_Back_Id, <parameters>) is begin case Id is when Foo_Id => Foo (<parameters>); when Bar_Id => Bar (<parameters>); ... end case; end Call_Back; In the data structure you store Id. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Newbie question: Implementing a callback mechanism with Ada 83 2007-02-10 19:38 Newbie question: Implementing a callback mechanism with Ada 83 benibilme 2007-02-10 20:40 ` Dmitry A. Kazakov @ 2007-02-11 2:46 ` Jeffrey R. Carter 2007-02-11 21:40 ` benibilme 1 sibling, 1 reply; 6+ messages in thread From: Jeffrey R. Carter @ 2007-02-11 2:46 UTC (permalink / raw) benibilme@gmail.com wrote: > > I need to develop an Ada application which must conform to Ada83. It > will be part of legacy system. I am very much new in Ada. The > application I have to develop is very much event driven. As an old > time C programmer I wanted to implement an event handling module which > you can subscribe callback functions and trigger the events when > necessary and unfortunately I have realized that Ada 83 does not > support pointer to subprograms. I have read about generics with which > defining subprograms as parameters of subprograms seems possible. On > the other hand, I could not figure out how to do it. Why do you want to register callbacks for the events? Does the processing for a given event change during execution? If not, there's no need for callbacks. The handling for a given event can know what subprogram to call. If they do, you no doubt have a fixed set of subprograms that can be needed for a given event, under a fixed set of conditions. You could have the handling for a given event test the condition and call the appropriate subprogram for the current condition. If you have a need to decouple the detection/generation of events from their handling, then the event package can add the events to a queue as they're detected, and the handling package can remove them from the queue and call the appropriate handling operation, which can test the condition and call the appropriate subprogram. If your system is concurrent, the queue can allow the caller to block when the queue is empty. This is often preferable to polling. If none of these will work, then I don't understand enough to make a meaningful comment. More detail from you might help alleviate that. -- Jeff Carter "C's solution to this [variable-sized array parameters] has real problems, and people who are complaining about safety definitely have a point." Dennis Ritchie 25 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Newbie question: Implementing a callback mechanism with Ada 83 2007-02-11 2:46 ` Jeffrey R. Carter @ 2007-02-11 21:40 ` benibilme 2007-02-12 5:03 ` Jeffrey R. Carter 2007-02-12 19:32 ` Adam Beneschan 0 siblings, 2 replies; 6+ messages in thread From: benibilme @ 2007-02-11 21:40 UTC (permalink / raw) 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 <stdio.h> #include <malloc.h> 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!" =============================================================================== ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Newbie question: Implementing a callback mechanism with Ada 83 2007-02-11 21:40 ` benibilme @ 2007-02-12 5:03 ` Jeffrey R. Carter 2007-02-12 19:32 ` Adam Beneschan 1 sibling, 0 replies; 6+ messages in thread From: Jeffrey R. Carter @ 2007-02-12 5:03 UTC (permalink / raw) benibilme@gmail.com wrote: > > 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. What you're describing here is more an implementation than a description of what you have to do. But to my mind, what you achieve through such an approach is not a reduction in coupling, but an increase. The events and their handling subprograms must be coupled in such situations. However, it is structural coupling to have package A call subprograms in package B; it is control coupling to have package B give package A data (subprogram access values) that determine what actions package B takes. > 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. I'm not prepared right now to wade through a bunch of C code for c.l.a. Besides, I'm not interested in how you would do something in C, but more in what you're trying to accomplish, so I can comment on something I do know, which is a good way to implement that in Ada. My personal bias is to avoid the use of access types and values whenever possible, and to hide them when not. What you describe here is a static relationship between events and their handling subprograms, and then you say that a static solution to this static problem is something you consider "ugly". To my mind, a dynamic solution to a static problem is unnecessary complexity; it violates the KISS principle and is ugly. Further, you think the static solution will be easier to read and more likely to be correct, but would prefer a less readable solution that is more likely to be incorrect. I find that incomprehensible. SW engineering emphasizes ease of reading over ease of writing, and Ada, a language for SW engineering, explicitly adopts this attitude. "Verbose" is not a bad word among the SW engineers here. Changing the action in response to an event in the static version involves looking at the code for the event and changing the subprogram that is called. In the callback case, it involves finding code not obviously related to the code for the event and changing the subprogram that is registered. The flexibility seems similar; the ease of making the change seems greater in the static case. It's possible I've missed something since I haven't studied your C code in any detail. > 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: This is not something I would recommend, especially if it adds complexity to an otherwise simple problem. -- Jeff Carter "Now look, Col. Batguano, if that really is your name." Dr. Strangelove 31 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Newbie question: Implementing a callback mechanism with Ada 83 2007-02-11 21:40 ` benibilme 2007-02-12 5:03 ` Jeffrey R. Carter @ 2007-02-12 19:32 ` Adam Beneschan 1 sibling, 0 replies; 6+ messages in thread From: Adam Beneschan @ 2007-02-12 19:32 UTC (permalink / raw) On Feb 11, 1:40 pm, "benibi...@gmail.com" <benibi...@gmail.com> wrote: > 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; This may or may not work; as far as I know, it's going to depend on your particular compiler vendor. You might need to add an Interface pragma, which may be called something different in different Ada implementations (this was replaced by the Import pragma in Ada 95): procedure Call_Handler (Event : Event_Type) is procedure Temp; pragma Interface (Ada, Temp); for Temp use at Event_Array(Event); begin ... If you just use the "use at", an Ada compiler *should* complain that there's no body for Temp---the existence of a "for ... use at" isn't supposed to be enough to tell the compiler that Temp's body will exist somewhere else. The Interface pragma should tell the compiler not to expect a body. However, this is a non-standard feature so there's no telling what compilers may or may not allow that they technically shouldn't have. Basically, you'll probably have to check your compiler implementation documentation or contact them and ask. Or you can just experiment to see if you can get the compiler to do what you want. If it were me, I'd try to find out why Ada 83 is such a hard-and-fast requirement---there isn't much downside to switching to Ada 95, as far as I can tell, unless you're on one of those obsolete machines for which the manufacturer never bothered to upgrade their Ada compiler to Ada 95. -- Adam ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2007-02-12 19:32 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2007-02-10 19:38 Newbie question: Implementing a callback mechanism with Ada 83 benibilme 2007-02-10 20:40 ` Dmitry A. Kazakov 2007-02-11 2:46 ` Jeffrey R. Carter 2007-02-11 21:40 ` benibilme 2007-02-12 5:03 ` Jeffrey R. Carter 2007-02-12 19:32 ` Adam Beneschan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox