Question

I was messing around with Ada, and I fount theGNAT.Ctrl_C package. I wanted to attempt to try it out since it seemed neat. I wrote the following program to test it out

with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Ctrl_C; use GNAT.Ctrl_C;

procedure Spam is
  procedure MyHandler is
  begin
    Put_Line("YOU WILL NEVER END MY SPAM");
  end MyHandler;
begin
Install_Handler(Handler => MyHandler);
while True loop
  Put_Line("SPAM SPAM SPAM SPAM SPAM SPAM");
end loop;
end Spam;

When I attempt to compile I get the errors "expected type Handler_Type defined at g-ctrl_c.ads:45" and found procedure name, probably missing Access attribute I have heard Ada has a super strict type system, but this has hindered me from learning the language for to long. If it helps the full text of GNAT.Ctrl_C is as follows:

------------------------------------------------------------------------------
--                                                                          --
--                         GNAT RUN-TIME COMPONENTS                         --
--                                                                          --
--                          G N A T . C T R L _ C                           --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--                      Copyright (C) 2002-2010, AdaCore                    --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

--  This package may be used to intercept the interruption of a running
--  program by the operator typing Control-C, without having to use an Ada
--  interrupt handler protected object.

--  This package is currently implemented under Windows and Unix platforms

--  Note concerning Unix systems:

--  The behavior of this package when using tasking depends on the interaction
--  between sigaction() and the thread library.

package GNAT.Ctrl_C is

  type Handler_Type is access procedure;
  --  Any parameterless library level procedure can be used as a handler.
  --  Handler_Type should not propagate exceptions.

  procedure Install_Handler (Handler : Handler_Type);
  --  Set up Handler to be called if the operator hits Ctrl-C, instead of the
  --  standard Control-C handler.

  procedure Uninstall_Handler;
  --  Reinstall the standard Control-C handler.
  --  If Install_Handler has never been called, this procedure has no effect.

  private
    pragma Import (C, Uninstall_Handler, "__gnat_uninstall_int_handler");
  end GNAT.Ctrl_C;
Was it helpful?

Solution

Install_Handler(Handler => MyHandler'Unrestricted_Access);

Unlike some languages, to get an access or "reference" or "pointer" to a procedure, you have to be explicit by using the 'Access attribute. It's a bit like using &func in C to get a pointer to a function, but C allows you to omit the &. Ada doesn't allow for that kind of shorthand, in part because Ada doesn't require parentheses when calling a procedure/function with no parameters, which means that using the name alone would be syntactically ambiguous.

In many cases, using 'Access would be good enough. The reason it isn't enough here, is because of Ada's accessibility rules. Suppose you write something like

procedure Spam is
  Number_Of_Vikings : Integer := 0;
  procedure MyHandler is
  begin
    Put_Line("YOU WILL NEVER END MY SPAM");
    Number_Of_Vikings := Number_Of_Vikings + 1;
  end MyHandler;
begin
  Install_Handler(Handler => MyHandler);
  --while True loop
  for I in 1 .. 10 loop
    Put_Line("SPAM SPAM SPAM SPAM SPAM SPAM");
  end loop;
end Spam;

In theory, Install_Handler could store the Handler parameter in a global, which means that (in theory) something else could use the access-to-procedure global to call MyHandler after the Spam procedure has returned. Then, MyHandler would try to do something with Number_Of_Vikings. But that variable would no longer exist, since it belongs to the Spam procedure which is no longer running. The result could be disastrous. (JavaScript has closures so it would keep the variable around even after Spam exits, and Java and C++11 also have limited forms of this, but Ada doesn't have anything equivalent.) Therefore, Ada has rules to prevent this sort of thing from happening. To get around the rules, you can use 'Unrestricted_Access (which is a GNAT attribute, not defined by the language), which tells the compiler "I know what I'm doing, don't worry about it".

OTHER TIPS

If the handler was moved to the "library level" (i.e. not embedded in your test program), it would be sufficient to add 'Access to the reference to it:

my_handler.ads:

procedure My_Handler;

my_handler.adb:

with Ada.Text_IO;
procedure My_Handler is
begin
   Ada.Text_IO.Put_Line ("YOU WILL NEVER END MY SPAM");
end My_Handler;

spam.adb:

with Ada.Text_IO;
with GNAT.Ctrl_C;
with My_Handler;
procedure Spam is
begin
   GNAT.Ctrl_C.Install_Handler (Handler => My_Handler'Access);
   loop
      Ada.Text_IO.Put_Line("SPAM SPAM SPAM SPAM SPAM SPAM");
   end loop;
end Spam;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top