Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

System.Address to Access to function/procedure conversion

1,036 views
Skip to first unread message

Tarek Ghaleb

unread,
Jul 28, 2013, 11:27:48 AM7/28/13
to
Hi Everyone,

This is my first post to comp.lang.ada and it is so embarrassingly
trivial. Given an Address of a function/procedure, how can you call it
or convert the address to an access?

I first thought of System.Address_To_Access_Conversions, but then it
doesn't seem to work for a function/procedure.

The way I finally did it was to wrap an access to the function in a
record and then pass the Address of the record, but there must be a
simpler way.

As for why not just pass an Access to the function directly, the
Address is actually passed to a C function which later passes it back
to Ada.

Tarek.

--
For fools rush in where angels fear to tread. -- Alexander Pope

--- news://freenews.netfront.net/ - complaints: ne...@netfront.net ---

Dmitry A. Kazakov

unread,
Jul 28, 2013, 12:14:16 PM7/28/13
to
On Sun, 28 Jul 2013 15:27:48 +0000 (UTC), Tarek Ghaleb wrote:

> As for why not just pass an Access to the function directly, the
> Address is actually passed to a C function which later passes it back
> to Ada.

If you pass anything to C you should better keep it under C convention,
even if you are certain that C does not touch it. You should pass an
access-to-procedure/function to C and get it back. The access type will
have pragma Convention (C, ... set. You don't need address here. E.g.

type Func is access procedure (...);
pragma Convention (C, Func);

procedure C_Stuff (...; Call_Back_Ptr : Func; ...);
pragma Import (C, C_Stuff, "fancy_c_library_entry");

The rationale is that, maybe unlikely still the access type might have a
different size than address and/or C pointer. When you use pragma
Convention (or the corresponding aspect if in Ada 2012) it is guaranteed to
work.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

Jeffrey Carter

unread,
Jul 28, 2013, 1:19:18 PM7/28/13
to
On 07/28/2013 08:27 AM, Tarek Ghaleb wrote:
>
> As for why not just pass an Access to the function directly, the
> Address is actually passed to a C function which later passes it back
> to Ada.

If you want any semblance of portability, then you should never pass an Address
to C. You should only pass a C-convention access type. The value must be stored
as a pointer on the C side, and there is no guarantee that Ada's Address has any
relationship to a C pointer. A C-convention access type, on the other hand, is
guaranteed to make sense as a C pointer.

--
Jeff Carter
"Oh Lord, bless this thy hand grenade, that with it thou
mayst blow thine enemies to tiny bits, in thy mercy."
Monty Python and the Holy Grail
24

Tarek Ghaleb

unread,
Jul 28, 2013, 3:05:44 PM7/28/13
to
On 2013-07-28, Dmitry A. Kazakov <mai...@dmitry-kazakov.de> wrote:
> If you pass anything to C you should better keep it under C convention,
> even if you are certain that C does not touch it. You should pass an
> access-to-procedure/function to C and get it back. The access type will
> have pragma Convention (C, ... set. You don't need address here. E.g.

I agree completely. Using C convention is the way to do it. But is it
*possible* to call a function using its address? (even if not a such a
great idea)

But let me explain how this question came about:

I've been experimenting with writing a thick binding for FLTK for a
couple of days now, just exploring the idea (which deserves a thread
by itself, since it would be interesting to get some
feedback). Anyway, one thing I started working on was callbacks, If I
use C convention the user of the binding will also have to declare his
procedure using C convention. For example (simplified)

procedure Cb_Button;
pragma Convention (C, Cb_Button); -- This is required I guess

and then later register the callback with

Btn.Callback(Cb_Button'Access);

But if I remove

pragma Conversion (C, Cb_Button);

I get

...: subprogram "Cb_Button" has wrong convention
...: does not match convention of access type

But I don't want the user to worry about using pragma Convention for
every defined callback procedure or think in terms of interfacing with
C (or in FLTK's case C++). Ideally, Callback() is not the actual
imported callback() but it takes Ada friendly parameters and does any
type conversions needed then calls the imported callback() function. I
thought maybe it works like above but without the convention line and
then Callback() would send the Address instead of the Access to the
actual callback(). But maybe there is a better way to do it.

There are other issues of course, for example: callbacks do take
parameters and some are void* but that would complicate my question
for the time being.

Tarek Ghaleb

unread,
Jul 28, 2013, 3:05:47 PM7/28/13
to
On 2013-07-28, Jeffrey Carter <spam.jrc...@spam.not.acm.org> wrote:
> If you want any semblance of portability, then you should never pass
> an Address to C. You should only pass a C-convention access type.

I agree, it is a much cleaner and portable way to do it.

> ... there is no guarantee that Ada's Address has any relationship to
> a C pointer.

Interesting, I thought System.Address was equivalent to a C pointer,
and I've seen it used that way in many snippets of code, etc. For
things like void*, if not represented as System.Address, how would it
be represented then?

Simon Wright

unread,
Jul 28, 2013, 3:23:15 PM7/28/13
to
Tarek Ghaleb <inv...@invalid.org> writes:

> Ideally, Callback() is not the actual imported callback() but it takes
> Ada friendly parameters and does any type conversions needed then
> calls the imported callback() function.

I find this confusing because I'm used to thinking of the procedure-to-
be-called-back as the 'callback', and what you're talking about here is
the registration interface. But if that's FLTK's nomenclature ...

Especially if the called-back procedure takes parameters, you're going
to have to do a conversion on the way back as well! I think you might be
able to do something with a generic, but .. you can't make a generic
formal type definition that declares an "access to procedure with this
parameter profile" type.

Dmitry A. Kazakov

unread,
Jul 28, 2013, 4:03:02 PM7/28/13
to
Usually C bindings pass an internal procedure of C convention. The user
provided callback is called from there. From my POV it is too low level for
good bindings. I prefer primitive operations of a tagged type instead of
using a raw subprogram. E.g. an interface with the operation Clicked passed
to button's On_Click.

type Button_Interface is limited interface;
procedure Clicked (Handler : in out Button_Interface;
Issuer : Widget'Class) is abstract;

type Button is ...
procedure On_Click (Widget : in out Button;
Handler : not null access Button_Interface'Class); -- [*]

Normally C library call takes an additional parameter void* passed as a
"User_Data" argument etc. Almost all C libraries provide such user data
arguments. This is a way to marshal parameters to the C callback from where
you call an Ada subprogram or dispatch.

Depending on what is the recipient of the callback you choose the strategy
of marshaling data through void*. A typical way for a widget library is
that when you connect to a signal emitted by a widget, a reference counted
object is allocated by the C library. The library deletes the object when
the signal gets disconnected or when the widget is destroyed. This object
is directly or indirectly pointed by the User_Data argument. There are
various issues when which object gets deallocated, which is not simple
stuff, but almost always necessary because user parameters never fit into
single void* argument.

When no user data argument is supported, you would use so-called
"trampolines", which is of course quite nasty stuff.

-------------
* You would like to wrap Button_Interface into a smart pointer object and
use the latter in On_Click.

Jeffrey Carter

unread,
Jul 28, 2013, 4:04:03 PM7/28/13
to
On 07/28/2013 12:05 PM, Tarek Ghaleb wrote:
>
> Interesting, I thought System.Address was equivalent to a C pointer,
> and I've seen it used that way in many snippets of code, etc. For
> things like void*, if not represented as System.Address, how would it
> be represented then?

Such code is compiler-dependent. GNAT usually (always?) maintains a
correspondence between hardware address, System.Address, access types, and C
pointers, but I've certainly used compilers where that wasn't true. I recall one
system written originally with GNAT that was ported to Rational (same platform).
The code used Unchecked_Conversion between access types and System.Address,
which failed miserably on Rational, where the 2 were not the same. It had to be
revised to use System.Address_To_Access_Conversions, which was not a trivial
undertaking. Moral: it's usually cheaper to write portable code in the 1st place.

A void* passed between C and Ada is usually C's equivalent of a private type,
with all values coming from C and never dereferenced by Ada. For such cases, all
that we need to model in Ada is that what we have is a C pointer. The
implementation usually looks like

type Void is null record;
type Void_Ptr is access all Void;
pragma Convention (C, Void_Ptr);

Of course, there's no real need for type Void; Void_Ptr could be "access all
Integer" and it would work just as well. The advantage of using Void is that
anyone who dereferences a Void_Ptr gets something useless.

If Void_Ptr is declared publicly, it should obviously be [limited] private. And
usually there's a better name for it than Void_Ptr.

Shark8

unread,
Jul 28, 2013, 5:13:22 PM7/28/13
to
On Sunday, July 28, 2013 1:05:44 PM UTC-6, Tarek Ghaleb wrote:
> On 2013-07-28, Dmitry A. Kazakov wrote:
>
> > If you pass anything to C you should better keep it under C convention,
> > even if you are certain that C does not touch it. You should pass an
> > access-to-procedure/function to C and get it back. The access type will
> > have pragma Convention (C, ... set. You don't need address here. E.g.
>
> I agree completely. Using C convention is the way to do it. But is it
> *possible* to call a function using its address? (even if not a such a
> great idea)

Yes. It's actually absurdly easy. Here's a sample of how to do it in Ada 2012, the same will work in earliet versions of Ada using Pargmas... all the way back to 83, IIUC.

-- Used a declare-block for expedience; I had a test-bed file open.
-- Be sure to WITH System.

ACTUAL_TEST:
declare

Procedure K with Convention => C;
Procedure K is
begin
Ada.Text_IO.Put_Line( "K was called" );
end K;

Procedure Execute( Addr : System.Address ) is
Procedure Stub with Import, Convention => C, Address => Addr;
Begin
Stub;
End Execute;

begin
Execute( K'Address );
end ACTUAL_TEST;

And there you are.

Maciej Sobczak

unread,
Jul 28, 2013, 5:31:00 PM7/28/13
to
> Given an Address of a function/procedure, how can you call it
> or convert the address to an access?

This article can be helpful:

http://www.inspirel.com/articles/Polymorphic_Callbacks_Ada_Cpp.html

The article goes even further than what you describe, as it shows polymorphic (object-oriented) callbacks, so in your case the actual solution might be one or two details simpler. In any case, it uses System.Address and standard Ada conversions to do the work.

--
Maciej Sobczak * http://www.msobczak.com * http://www.inspirel.com

Tarek Ghaleb

unread,
Jul 28, 2013, 6:03:39 PM7/28/13
to
On 2013-07-28, Simon Wright <si...@pushface.org> wrote:
> Tarek Ghaleb <inv...@invalid.org> writes:
>
>> Ideally, Callback() is not the actual imported callback() but it takes
>> Ada friendly parameters and does any type conversions needed then
>> calls the imported callback() function.
>
> I find this confusing because I'm used to thinking of the procedure-to-
> be-called-back as the 'callback', and what you're talking about here is
> the registration interface. But if that's FLTK's nomenclature ...

Yes, this is the naming for FLTK, but I can change that I guess. I
probably should have used a different name here for clarity, Register()
perhaps or Register_Callback().

> Especially if the called-back procedure takes parameters, you're going
> to have to do a conversion on the way back as well!

Initially, I thought it was simple, but now it doesn't seem so easy
:-) I'd want to wrap the user callback, Button_Cb() for example, in
another procedure that fits the profile required by the imported
callback registration function and does that conversion and calling of
Button_Cb().

One idea of the Registration procedure, stripped down and not
considering parameters and parameter conversion (but saves the user
from having to use pragma Convention C) is

procedure Register_Callback (Proc : access procedure) is

procedure Cb_Wrapper;
pragma Convention (C, Cb_Wrapper);

procedure Cb_Wrapper is
begin
Proc.all;
end Cb_Wrapper;
begin
imported_callback_registration (Cb_Wrapper'Access);
end Register_Callback;

But this of course wouldn't work, since the Wrapper is allocated on
the stack and is deeper than access type.

> I think you might be able to do something with a generic, but .. you
> can't make a generic formal type definition that declares an "access
> to procedure with this parameter profile" type.

A generic used in which way? I'd like Register_Callback() to be
callable simply with an access to a procedure that fits a certain
profile with the caller not having to worry about details. The wrapper
could be a generic maybe. There are a couple of other ideas but
nothing promising so far.

Tarek.

--
The opposite of a correct statement is a false statement. But the opposite
of a profound truth may well be another profound truth. -- Niels Bohr

Tarek Ghaleb

unread,
Jul 28, 2013, 6:54:27 PM7/28/13
to
On 2013-07-28, Shark8 <onewing...@gmail.com> wrote:
>> I agree completely. Using C convention is the way to do it. But is it
>> *possible* to call a function using its address? (even if not a such a
>> great idea)

> Yes. It's actually absurdly easy. Here's a sample of how to do it in
> Ada 2012, the same will work in earliet versions of Ada using
> Pargmas... all the way back to 83, IIUC.

> ACTUAL_TEST:
> declare
>
> Procedure K with Convention => C;
> Procedure K is
> begin
> Ada.Text_IO.Put_Line( "K was called" );
> end K;
>
> Procedure Execute( Addr : System.Address ) is
> Procedure Stub with Import, Convention => C, Address => Addr;
> Begin
> Stub;
> End Execute;
>
> begin
> Execute( K'Address );
> end ACTUAL_TEST;

Great! I didn't know about the Address Aspect. I don't think it exists
in pragma form, though. At least I couldn't find it in the RM; but
thanks, that answers my question.

Tarek.

--
Aim for the moon. If you miss, you may hit a star. -- W. Clement Stone

Shark8

unread,
Jul 28, 2013, 11:27:26 PM7/28/13
to
On Sunday, July 28, 2013 4:54:27 PM UTC-6, Tarek Ghaleb wrote:
> On 2013-07-28, Shark8 wrote:
>
> >> I agree completely. Using C convention is the way to do it. But is it
> >> *possible* to call a function using its address? (even if not a such a
> >> great idea)
>
> > Yes. It's actually absurdly easy. Here's a sample of how to do it in
> > Ada 2012, the same will work in earliet versions of Ada using
> > Pargmas... all the way back to 83, IIUC.

Oops; a little overzealous there.
You have to use attribute definition clauses, which IIRC came in Ada 95.
Below compiles and runs in GNAT using Ada83-mode:

-- Pragma Ada_2012;
Pragma Ada_83;
Pragma Assertion_Policy( Check );

With
Text_IO,
System;

Procedure Test is

Begin
Text_IO.Put_Line("Starting Test:");


ACTUAL_TEST:
declare

-- Note: Pragma CONVENTION is non-standard.
Procedure K;
Pragma Convention( C, K );

Procedure K is
begin
Text_IO.Put_Line( "K was called" );
end K;

Procedure Execute( Addr : System.Address ) is
-- Note: Pragma IMPORT is non-standard.
Procedure Stub;
Pragma Import( C, Stub );

-- Note: The Ada 83 LRM, 13.5 (Address Clauses) gives the following:
-- for CONTROL use at 16#0020#; -- assuming that SYSTEM.ADDRESS is an integer
-- with the following warning:
-- Address clauses should not be used to achieve overlays of
-- objects or overlays of program units. Nor should a given
-- interrupt be linked to more than one entry. Any program using
-- address clauses to achieve such effects is erroneous.
--
-- In Ada 95 we get propper Attribute Definition Clauses and so
-- we would use the following:
-- For Stub'Address Use Addr;
For Stub use at Addr;

Use Text_IO;
Begin
Put_Line( "Executing:" );
Put( ASCII.HT );
Stub;
End Execute;

begin
Execute( K'Address );
end ACTUAL_TEST;


Text_IO.Put_Line("Testing complete.");
End Test;

Tarek Ghaleb

unread,
Jul 29, 2013, 2:26:41 AM7/29/13
to
On 2013-07-28, Maciej Sobczak <see.my....@gmail.com> wrote:
>> Given an Address of a function/procedure, how can you call it
>> or convert the address to an access?

> This article can be helpful:
> http://www.inspirel.com/articles/Polymorphic_Callbacks_Ada_Cpp.html

> The article goes even further than what you describe, as it shows
> polymorphic (object-oriented) callbacks, so in your case the actual
> solution might be one or two details simpler. In any case, it uses
> System.Address and standard Ada conversions to do the work.

Yes, the article is very relevant to what I'm doing. It's very
interesting. Especially, that it's about callbacks and C++, which is
exactly what I'm dealing with. Thanks for the link.

Tarek.

--
Rotten wood cannot be carved. -- Confucius, "Analects", Book 5, Ch. 9

Tarek Ghaleb

unread,
Jul 29, 2013, 3:06:06 AM7/29/13
to
> -- SYSTEM.ADDRESS is an integer
> -- with the following warning:
> -- Address clauses should not be used to achieve overlays of
> -- objects or overlays of program units. Nor should a given
> -- interrupt be linked to more than one entry. Any program using
> -- address clauses to achieve such effects is erroneous.
>
> Use Text_IO;
> Begin
> Put_Line( "Executing:" );
> Put( ASCII.HT );
> Stub;
> End Execute;
>
> begin
> Execute( K'Address );
> end ACTUAL_TEST;
>
>
> Text_IO.Put_Line("Testing complete.");
> End Test;

That's an _exhaustively_ thorough answer :-))

Actually, I've used

For A'Address Use B'Address;

before, but never thought of using it for a subprogram: the way you
showed in the example above.

Tarek.

--
Rotten wood cannot be carved. -- Confucius, "Analects", Book 5, Ch. 9

0 new messages