comp.lang.ada
 help / color / mirror / Atom feed
From: "benibilme@gmail.com" <benibilme@gmail.com>
Subject: Re: Newbie question: Implementing a callback mechanism with Ada 83
Date: 11 Feb 2007 13:40:53 -0800
Date: 2007-02-11T13:40:53-08:00	[thread overview]
Message-ID: <1171230053.064136.26460@v33g2000cwv.googlegroups.com> (raw)
In-Reply-To: <Yrvzh.268605$aJ.222221@attbi_s21>

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!"

===============================================================================








  reply	other threads:[~2007-02-11 21:40 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2007-02-12  5:03     ` Jeffrey R. Carter
2007-02-12 19:32     ` Adam Beneschan
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox