comp.lang.ada
 help / color / mirror / Atom feed
From: Adam Beneschan <adam@irvine.com>
Subject: Re: Access to procedure and generics
Date: Tue, 28 Aug 2012 08:57:35 -0700 (PDT)
Date: 2012-08-28T08:57:35-07:00	[thread overview]
Message-ID: <647e1a9e-e8be-4ad2-a9d4-21c8ae6f52f2@googlegroups.com> (raw)
In-Reply-To: <k1i41v$sfk$1@speranza.aioe.org>

On Tuesday, August 28, 2012 2:46:42 AM UTC-7, Markus Schöpflin wrote:
> Hello,
> 
> please consider the following program. What it is trying to achieve (in Ada 
> 95) is that a callback is registered for call as soon as the generic is 
> instantiated.
> 
> ---%<---
> 
> package CALLBACK is
>     type CALLBACK_T is access procedure;
>     procedure REGISTER_CALLBACK(CALLBACK : CALLBACK_T);
> end CALLBACK;
> 
> --
> 
> package body CALLBACK is
>     procedure REGISTER_CALLBACK(CALLBACK : CALLBACK_T)
>     is
>     begin
>        null;
>     end;
> end CALLBACK;
> 
> --
> 
> generic
> package USE_CALLBACK is
>     procedure HANDLER;
> end USE_CALLBACK;
> 
> --
> 
> with CALLBACK;
> package body USE_CALLBACK
> is
>     procedure HANDLER is
>     begin
>        null;
>     end;
> begin
>     CALLBACK.REGISTER_CALLBACK(HANDLER'Access);
> end USE_CALLBACK;
> 
> --
> 
> with USE_CALLBACK;
> procedure TEST is
>     package FOO is new USE_CALLBACK;
> begin
>     null;
> end TEST;
> 
> --->%---
> 
> 
> 
> When trying to compile this with GNAT 4.4.5 in Ada95 mode, I get the following 
> error:
> 
>  > gnatmake -gnat95 test
> 
> gcc-4.4 -c -gnat95 test.adb
> 
> use_callback.adb:10:38: 'Access attribute not allowed in generic body 
> use_callback.adb:10:38: because access type "CALLBACK_T" is declared outside 
> generic unit (RM 3.10.2(32))
> use_callback.adb:10:38: move 'Access to private part, or (Ada 2005) use 
> anonymous access type instead of "CALLBACK_T"
> 
> Now, after reading the reference cited by GNAT I think I understand why it 
> doesn't compile, but I have a hard time figuring out what GNAT means by "move 
> 'Access to private part".
> 
> And I'm still at a loss on how to achieve what I actually need, namely 
> automatically registering a callback as soon as the generic is instantiated.
> 
> Any help would be much appreciated.

The problem, as you may have figured out, is that Callback_T is a *global* access type, and you can't have a global access type that could point to a local subprogram (one that is nested within another subprogram).  The reason is that on many implementations, the local subprogram requires a "static link" parameter so that the local subprogram can access variables declared inside the subprogram that contains it.  If code calls a subprogram through Callback_T, it won't know what the static link is, and the result would be complete havoc.  That's why this can't be allowed.

The problem with your generic code is that the generic *could* be instantiated inside a subprogram, using a "local subprogram" as the HANDLER parameter.  That's why the rule in 3.10.2(32) is there, to prevent this from happening.  And the "contract model" principle of Ada says that if a generic is legal, and the specification is legal when you instantiate it, then the instantiation is legal because legality rules aren't checked in the body.  That's why there have to be rules to make certain generics illegal, because they *could* cause problems if you instantiate them inside a subprogram.

Moving the 'Access to the specification (including the private part) solves the contract model problem; the language allows this because it's no longer in the body and thus *can* be checked when you instantiate the generic.  Egil's solution will work as long as you never instantiate the generic inside a subprogram.  But you did instantiate it inside the Test procedure, so you still got the error.

'Unrestricted_Access may work on GNAT, because of the way they implement things.  Callback_T is still a simple pointer that doesn't have a static link, and the routine it points to needs a static link, but GNAT uses some trickery that, I think, involves creating a code thunk (trampoline) at runtime to get everything set up.  Although ICC Ada supports 'Unrestricted_Access, it wouldn't work in that case because ICC Ada doesn't generate trampolines.  However, there are ways to tell the compiler that the handler doesn't need a static link, and then you could get it to work.  None of this is portable, though.

If you want a portable solution, you can use tagged types instead of access-to-procedure:

package Callback is
    type Callback_Object is interface;
    type Callback_T is access all Callback_Object'Class;
    procedure Handler (Obj : in out Callback_Object) is abstract;
    procedure Register_Callback(Callback : Callback_T);
end Callback;

-- if Z is a registered Callback_T, Z.Handler will call the handler

generic
package USE_CALLBACK is
    procedure HANDLER;
end USE_CALLBACK;
--
with CALLBACK;
package body USE_CALLBACK
is
    procedure HANDLER is
    begin
       ...
    end;

    type Call_Handler is new Callback.Callback_Object with null record;
    overriding
    procedure Handler (Obj : in out Call_Handler) is
    begin
        Handler; -- of course, this doesn't *have* to have the same name
    end Handler;

    X : aliased Call_Handler;

begin
    CALLBACK.REGISTER_CALLBACK(X'Unchecked_Access);
end USE_CALLBACK;

Now you can instantiate Use_Callback inside or outside a subprogram, and it should still work on any Ada implementation.  (Credit to Brad Moore who came up with essentially this solution to a similar problem.)  It's still unsafe, though.  NOTE WELL that if the Test procedure finishes, and your callback pointer is still registered, and it the registered callback gets used after Test is done, all hell will break loose.  But that's true about any solution, including those involving 'Unrestricted_Access.  If you want a way to instantiate the generic inside Test, and have the generic register a handler that will still work OK even after Test has returned, you'll just have to rearrange the program.

                           -- Adam








  parent reply	other threads:[~2012-08-28 15:57 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-28  9:46 Access to procedure and generics Markus Schöpflin
2012-08-28 10:20 ` Egil Høvik
2012-08-28 10:22   ` Egil Høvik
2012-08-28 10:32     ` Markus Schöpflin
2012-08-31 14:14       ` Robert A Duff
2012-08-28 10:41 ` Georg Bauhaus
2012-08-28 10:47   ` Markus Schöpflin
2012-08-28 15:57 ` Adam Beneschan [this message]
2012-08-28 16:01   ` Adam Beneschan
2012-08-29  3:52   ` Brad Moore
2012-08-29 11:25   ` Markus Schöpflin
replies disabled

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