From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,b9a378a9a09dab87 X-Google-NewGroupId: yes X-Google-Attributes: gida07f3367d7,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII Received: by 10.66.74.41 with SMTP id q9mr2872131pav.41.1346169456864; Tue, 28 Aug 2012 08:57:36 -0700 (PDT) Received: by 10.66.87.161 with SMTP id az1mr737424pab.38.1346169456380; Tue, 28 Aug 2012 08:57:36 -0700 (PDT) Received: by 10.68.135.103 with SMTP id pr7mr1584259pbb.7.1346169456363; Tue, 28 Aug 2012 08:57:36 -0700 (PDT) Path: a5ni21147pbv.0!nntp.google.com!news2.google.com!4no23097164pbn.1!news-out.google.com!t10ni19709575pbh.0!nntp.google.com!4no23097152pbn.1!postnews.google.com!glegroupsg2000goo.googlegroups.com!not-for-mail Newsgroups: comp.lang.ada Date: Tue, 28 Aug 2012 08:57:35 -0700 (PDT) In-Reply-To: Complaints-To: groups-abuse@google.com Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=66.126.103.122; posting-account=duW0ogkAAABjRdnxgLGXDfna0Gc6XqmQ NNTP-Posting-Host: 66.126.103.122 References: User-Agent: G2/1.0 MIME-Version: 1.0 Message-ID: <647e1a9e-e8be-4ad2-a9d4-21c8ae6f52f2@googlegroups.com> Subject: Re: Access to procedure and generics From: Adam Beneschan Injection-Date: Tue, 28 Aug 2012 15:57:36 +0000 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Date: 2012-08-28T08:57:35-07:00 List-Id: On Tuesday, August 28, 2012 2:46:42 AM UTC-7, Markus Sch=F6pflin wrote: > Hello, >=20 > please consider the following program. What it is trying to achieve (in A= da=20 > 95) is that a callback is registered for call as soon as the generic is= =20 > instantiated. >=20 > ---%<--- >=20 > package CALLBACK is > type CALLBACK_T is access procedure; > procedure REGISTER_CALLBACK(CALLBACK : CALLBACK_T); > end CALLBACK; >=20 > -- >=20 > package body CALLBACK is > procedure REGISTER_CALLBACK(CALLBACK : CALLBACK_T) > is > begin > null; > end; > end CALLBACK; >=20 > -- >=20 > generic > package USE_CALLBACK is > procedure HANDLER; > end USE_CALLBACK; >=20 > -- >=20 > with CALLBACK; > package body USE_CALLBACK > is > procedure HANDLER is > begin > null; > end; > begin > CALLBACK.REGISTER_CALLBACK(HANDLER'Access); > end USE_CALLBACK; >=20 > -- >=20 > with USE_CALLBACK; > procedure TEST is > package FOO is new USE_CALLBACK; > begin > null; > end TEST; >=20 > --->%--- >=20 >=20 >=20 > When trying to compile this with GNAT 4.4.5 in Ada95 mode, I get the foll= owing=20 > error: >=20 > > gnatmake -gnat95 test >=20 > gcc-4.4 -c -gnat95 test.adb >=20 > use_callback.adb:10:38: 'Access attribute not allowed in generic body=20 > use_callback.adb:10:38: because access type "CALLBACK_T" is declared outs= ide=20 > generic unit (RM 3.10.2(32)) > use_callback.adb:10:38: move 'Access to private part, or (Ada 2005) use= =20 > anonymous access type instead of "CALLBACK_T" >=20 > Now, after reading the reference cited by GNAT I think I understand why i= t=20 > doesn't compile, but I have a hard time figuring out what GNAT means by "= move=20 > 'Access to private part". >=20 > And I'm still at a loss on how to achieve what I actually need, namely=20 > automatically registering a callback as soon as the generic is instantiat= ed. >=20 > 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 reaso= n 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 instantia= ted inside a subprogram, using a "local subprogram" as the HANDLER paramete= r. That's why the rule in 3.10.2(32) is there, to prevent this from happen= ing. And the "contract model" principle of Ada says that if a generic is l= egal, and the specification is legal when you instantiate it, then the inst= antiation 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 the= y *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 longe= r 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 insi= de 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 th= ings. 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 tric= kery 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 nee= d a static link, and then you could get it to work. None of this is portab= le, 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 ca= me up with essentially this solution to a similar problem.) It's still uns= afe, though. NOTE WELL that if the Test procedure finishes, and your callb= ack pointer is still registered, and it the registered callback gets used a= fter Test is done, all hell will break loose. But that's true about any so= lution, including those involving 'Unrestricted_Access. If you want a way = to instantiate the generic inside Test, and have the generic register a han= dler that will still work OK even after Test has returned, you'll just have= to rearrange the program. -- Adam