comp.lang.ada
 help / color / mirror / Atom feed
* Access to procedure and generics
@ 2012-08-28  9:46 Markus Schöpflin
  2012-08-28 10:20 ` Egil Høvik
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Markus Schöpflin @ 2012-08-28  9:46 UTC (permalink / raw)


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.

Regards,
Markus



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  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:41 ` Georg Bauhaus
  2012-08-28 15:57 ` Adam Beneschan
  2 siblings, 1 reply; 11+ messages in thread
From: Egil Høvik @ 2012-08-28 10:20 UTC (permalink / raw)


How about this:

with Callback;
generic 
package USE_CALLBACK is 
    procedure HANDLER; 
private
    The_Handler : constant Callback.Callback_t := Handler'Access;
end USE_CALLBACK; 
-- 
with CALLBACK; 
package body USE_CALLBACK 
is 
    procedure HANDLER is 
    begin 
       null; 
    end; 
begin 
    CALLBACK.REGISTER_CALLBACK(The_Handler'Access); 
end USE_CALLBACK; 
-- 

-- 
~egilhh



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  2012-08-28 10:20 ` Egil Høvik
@ 2012-08-28 10:22   ` Egil Høvik
  2012-08-28 10:32     ` Markus Schöpflin
  0 siblings, 1 reply; 11+ messages in thread
From: Egil Høvik @ 2012-08-28 10:22 UTC (permalink / raw)



>   CALLBACK.REGISTER_CALLBACK(The_Handler'Access); 

Should of course be: 
CALLBACK.REGISTER_CALLBACK(The_Handler);




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  2012-08-28 10:22   ` Egil Høvik
@ 2012-08-28 10:32     ` Markus Schöpflin
  2012-08-31 14:14       ` Robert A Duff
  0 siblings, 1 reply; 11+ messages in thread
From: Markus Schöpflin @ 2012-08-28 10:32 UTC (permalink / raw)


Am 28.08.2012 12:22, schrieb Egil H�vik:
>
>>    CALLBACK.REGISTER_CALLBACK(The_Handler'Access);
>
> Should of course be:
> CALLBACK.REGISTER_CALLBACK(The_Handler);
>

Nope, doesn't work.

 > gnatmake -gnat95 test
gcc-4.4 -c -gnat95 test.adb
test.adb:4:04: instantiation error at use_callback.ads:7
test.adb:4:04: subprogram must not be deeper than access type

where use_callback.ads:7 looks like:

    THE_HANDLER : constant CALLBACK.CALLBACK_T := HANDLER'Access;





^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  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:41 ` Georg Bauhaus
  2012-08-28 10:47   ` Markus Schöpflin
  2012-08-28 15:57 ` Adam Beneschan
  2 siblings, 1 reply; 11+ messages in thread
From: Georg Bauhaus @ 2012-08-28 10:41 UTC (permalink / raw)


On 28.08.12 11:46, Markus Schï¿œpflin wrote:
> 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.


This question seems to be addressed by (unsafe) 'Unrestricted_Access,
which GNAT and ICC's compiler both support, IIRC.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  2012-08-28 10:41 ` Georg Bauhaus
@ 2012-08-28 10:47   ` Markus Schöpflin
  0 siblings, 0 replies; 11+ messages in thread
From: Markus Schöpflin @ 2012-08-28 10:47 UTC (permalink / raw)


Am 28.08.2012 12:41, schrieb Georg Bauhaus:
> On 28.08.12 11:46, Markus Schï¿œpflin wrote:
>> 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.
>
>
> This question seems to be addressed by (unsafe) 'Unrestricted_Access,
> which GNAT and ICC's compiler both support, IIRC.

D'oh! I knew I missed something obvious.

Thanks,
Markus




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  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:41 ` Georg Bauhaus
@ 2012-08-28 15:57 ` Adam Beneschan
  2012-08-28 16:01   ` Adam Beneschan
                     ` (2 more replies)
  2 siblings, 3 replies; 11+ messages in thread
From: Adam Beneschan @ 2012-08-28 15:57 UTC (permalink / raw)


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








^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  2012-08-28 15:57 ` Adam Beneschan
@ 2012-08-28 16:01   ` Adam Beneschan
  2012-08-29  3:52   ` Brad Moore
  2012-08-29 11:25   ` Markus Schöpflin
  2 siblings, 0 replies; 11+ messages in thread
From: Adam Beneschan @ 2012-08-28 16:01 UTC (permalink / raw)


On Tuesday, August 28, 2012 8:57:36 AM UTC-7, Adam Beneschan wrote:

> The problem with your generic code is that the generic *could* be instantiated 
> inside a subprogram, using a "local subprogram" as the HANDLER parameter.

This was worded badly.  

The problem with your generic code is that the generic *could* be instantiated inside a subprogram, and then HANDLER would become a "local subprogram", which means Handler'Access can't be used as a global access type.

                            -- Adam



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  2012-08-28 15:57 ` Adam Beneschan
  2012-08-28 16:01   ` Adam Beneschan
@ 2012-08-29  3:52   ` Brad Moore
  2012-08-29 11:25   ` Markus Schöpflin
  2 siblings, 0 replies; 11+ messages in thread
From: Brad Moore @ 2012-08-29  3:52 UTC (permalink / raw)


On 28/08/2012 9:57 AM, Adam Beneschan wrote:
> 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.)

I have to give credit where credit is due. It was Steve Baird who 
suggested to me that I should try to use tagged types as an approach to 
get around this issue.

Brad



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  2012-08-28 15:57 ` Adam Beneschan
  2012-08-28 16:01   ` Adam Beneschan
  2012-08-29  3:52   ` Brad Moore
@ 2012-08-29 11:25   ` Markus Schöpflin
  2 siblings, 0 replies; 11+ messages in thread
From: Markus Schöpflin @ 2012-08-29 11:25 UTC (permalink / raw)


Am 28.08.2012 17:57, schrieb Adam Beneschan:

[snip excellent explanation]

Adam,

thank you very much for your detailed explanation of the problem and the 
pointers to possible solutions.

Unfortunately, the generic interface is basically cast in stone, but after 
realizing that I can move the *instantiation* of the generic to its own 
package, I'm perfectly OK with the following solution

---%<---
with CALLBACK;
generic
package USE_CALLBACK is
    procedure HANDLER;
private
    CALLBACK_ACCESS : constant CALLBACK.CALLBACK_T := HANDLER'Access;
end USE_CALLBACK;
--->%---

suggested by Georg.

Regards,
Markus



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Access to procedure and generics
  2012-08-28 10:32     ` Markus Schöpflin
@ 2012-08-31 14:14       ` Robert A Duff
  0 siblings, 0 replies; 11+ messages in thread
From: Robert A Duff @ 2012-08-31 14:14 UTC (permalink / raw)


Markus Sch�pflin <no.spam@spam.spam> writes:

> Am 28.08.2012 12:22, schrieb Egil H�vik:
>>
>>>    CALLBACK.REGISTER_CALLBACK(The_Handler'Access);
>>
>> Should of course be:
>> CALLBACK.REGISTER_CALLBACK(The_Handler);
>>
>
> Nope, doesn't work.

In addition to declaring the constant, you need to move the
instantiation to a non-nested place (in some library package,
rather than inside a procedure).

- bob



^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2012-09-07 12:07 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
2012-08-28 16:01   ` Adam Beneschan
2012-08-29  3:52   ` Brad Moore
2012-08-29 11:25   ` Markus Schöpflin

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