comp.lang.ada
 help / color / mirror / Atom feed
* How can one record component be local and another not?
@ 2020-05-05 11:04 hreba
  2020-05-05 11:33 ` AdaMagica
                   ` (5 more replies)
  0 siblings, 6 replies; 39+ messages in thread
From: hreba @ 2020-05-05 11:04 UTC (permalink / raw)


I have reduced my problem to the following example:

package Aux is
    type Integer_P is access all Integer;
    type Rec is record
       a: aliased Integer;
       p: Integer_P;
    end record;

    procedure Init (r: in out Rec);
end Aux;

package body Aux is
    procedure Init (r: in out Rec) is
    begin
       r.p:= r.a'Access;  -- <-- error!
    end Init;
end Aux;

Compiling results in "non-local pointer cannot point to local object" 
indicating the marked line above.

How can one record component be local and another not?

Intention is to have a library subprogram Init() for initialization. How 
would I implement that instead?

(The reason for the above construction is the need to pass a pointer to 
a C library function.)

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 11:04 How can one record component be local and another not? hreba
@ 2020-05-05 11:33 ` AdaMagica
  2020-05-05 11:38   ` AdaMagica
                     ` (2 more replies)
  2020-05-05 11:43 ` AdaMagica
                   ` (4 subsequent siblings)
  5 siblings, 3 replies; 39+ messages in thread
From: AdaMagica @ 2020-05-05 11:33 UTC (permalink / raw)


Am Dienstag, 5. Mai 2020 13:04:56 UTC+2 schrieb hreba:
> I have reduced my problem to the following example:
> 
> package Aux is
>     type Integer_P is access all Integer;
>     type Rec is record
>        a: aliased Integer;
>        p: Integer_P;
>     end record;
> 
>     procedure Init (r: in out Rec);
> end Aux;
> 
> package body Aux is
>     procedure Init (r: in out Rec) is
>     begin
>        r.p:= r.a'Access;  -- <-- error!
>     end Init;
> end Aux;

Imagine you define a local Rec:

with Aux;
procedure Proc is
  R: Aux.Rec;
begin
  Aux.Init (R);
end Proc;

Now R has shorter lifetime than the access type Integer_P. Thus you might have somewhere an access object AO with longer lifetime than R and try to assign
AO := R.P;

Try Unrestricted_Access.

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

* Re: How can one record component be local and another not?
  2020-05-05 11:33 ` AdaMagica
@ 2020-05-05 11:38   ` AdaMagica
  2020-05-05 12:59   ` hreba
  2020-05-05 14:32   ` hreba
  2 siblings, 0 replies; 39+ messages in thread
From: AdaMagica @ 2020-05-05 11:38 UTC (permalink / raw)


Am Dienstag, 5. Mai 2020 13:33:08 UTC+2 schrieb AdaMagica:
> Try Unrestricted_Access.

But this will only work if Rec parameters are not transferred by copy.

So perhaps this might be necessary (I'm not sure this helps):
    procedure Init (r: in out aliased Rec); 

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

* Re: How can one record component be local and another not?
  2020-05-05 11:04 How can one record component be local and another not? hreba
  2020-05-05 11:33 ` AdaMagica
@ 2020-05-05 11:43 ` AdaMagica
  2020-05-05 12:55   ` hreba
  2020-05-05 11:46 ` Simon Wright
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 39+ messages in thread
From: AdaMagica @ 2020-05-05 11:43 UTC (permalink / raw)


Das ganze Konstrukt sieht sehr gefährlich aus! Copies will not point to the correct component:

A, B: Rec;

A := B;  -- Peng!

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

* Re: How can one record component be local and another not?
  2020-05-05 11:04 How can one record component be local and another not? hreba
  2020-05-05 11:33 ` AdaMagica
  2020-05-05 11:43 ` AdaMagica
@ 2020-05-05 11:46 ` Simon Wright
  2020-05-05 13:07   ` hreba
  2020-05-05 11:48 ` Niklas Holsti
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 39+ messages in thread
From: Simon Wright @ 2020-05-05 11:46 UTC (permalink / raw)


hreba <f_hreba@yahoo.com.br> writes:

> package body Aux is
>    procedure Init (r: in out Rec) is
>    begin
>       r.p:= r.a'Access;  -- <-- error!
>    end Init;
> end Aux;

'Unchecked_Access does the job (but this is outside my language-lawyering
abilities!)

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

* Re: How can one record component be local and another not?
  2020-05-05 11:04 How can one record component be local and another not? hreba
                   ` (2 preceding siblings ...)
  2020-05-05 11:46 ` Simon Wright
@ 2020-05-05 11:48 ` Niklas Holsti
  2020-05-05 13:44   ` hreba
  2020-05-05 15:45 ` Jeffrey R. Carter
  2020-05-05 17:32 ` hreba
  5 siblings, 1 reply; 39+ messages in thread
From: Niklas Holsti @ 2020-05-05 11:48 UTC (permalink / raw)


On 2020-05-05 14:04, hreba wrote:
> I have reduced my problem to the following example:
> 
> package Aux is
>     type Integer_P is access all Integer;
>     type Rec is record
>        a: aliased Integer;
>        p: Integer_P;
>     end record;
> 
>     procedure Init (r: in out Rec);
> end Aux;
> 
> package body Aux is
>     procedure Init (r: in out Rec) is
>     begin
>        r.p:= r.a'Access;  -- <-- error!
>     end Init;
> end Aux;
> 
> Compiling results in "non-local pointer cannot point to local object" 
> indicating the marked line above.
> 
> How can one record component be local and another not?
> 
> Intention is to have a library subprogram Init() for initialization. How 
> would I implement that instead?
> 
> (The reason for the above construction is the need to pass a pointer to 
> a C library function.)
> 

The parameter "r" might be passed by copy, in which case the r.a'Access 
would point to the local copy, and become a dangling pointer on return 
(copy-out).

It works if you replace "r: in out" with "r : access", which ensures 
pass by reference.

It did not work for me (same error message) if I just added "aliased", 
making "r : aliased in out Rec", although I think that should also be 
enough to ensure pass by reference.

This was on GNAT Community 2019 (20190517-83), Mac.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-05 11:43 ` AdaMagica
@ 2020-05-05 12:55   ` hreba
  0 siblings, 0 replies; 39+ messages in thread
From: hreba @ 2020-05-05 12:55 UTC (permalink / raw)


On 5/5/20 1:43 PM, AdaMagica wrote:
> Das ganze Konstrukt sieht sehr gefährlich aus! Copies will not point to the correct component:
> 
> A, B: Rec;
> 
> A := B;  -- Peng!
> 

That is easy to solve: In my real application, Rec would be private 
anyway, so it could be made limited, and Init would be changed into a 
constructor function with result Rec. But first I wanted to solve (and 
understand) that local/non-local issue.

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 11:33 ` AdaMagica
  2020-05-05 11:38   ` AdaMagica
@ 2020-05-05 12:59   ` hreba
  2020-05-05 13:19     ` J-P. Rosen
  2020-05-05 13:37     ` Jere
  2020-05-05 14:32   ` hreba
  2 siblings, 2 replies; 39+ messages in thread
From: hreba @ 2020-05-05 12:59 UTC (permalink / raw)


On 5/5/20 1:33 PM, AdaMagica wrote:
> Am Dienstag, 5. Mai 2020 13:04:56 UTC+2 schrieb hreba:
>> I have reduced my problem to the following example:
>>
>> package Aux is
>>      type Integer_P is access all Integer;
>>      type Rec is record
>>         a: aliased Integer;
>>         p: Integer_P;
>>      end record;
>>
>>      procedure Init (r: in out Rec);
>> end Aux;
>>
>> package body Aux is
>>      procedure Init (r: in out Rec) is
>>      begin
>>         r.p:= r.a'Access;  -- <-- error!
>>      end Init;
>> end Aux;
> 
> Imagine you define a local Rec:
> 
> with Aux;
> procedure Proc is
>    R: Aux.Rec;
> begin
>    Aux.Init (R);
> end Proc;
> 
> Now R has shorter lifetime than the access type Integer_P. Thus you might have somewhere an access object AO with longer lifetime than R and try to assign
> AO := R.P;
> 
> Try Unrestricted_Access.
> 

Don't understand that. R.a and R.p have exactly the same lifetime, which 
is the lifetime of R, haven't they?

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 11:46 ` Simon Wright
@ 2020-05-05 13:07   ` hreba
  2020-05-05 17:00     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 39+ messages in thread
From: hreba @ 2020-05-05 13:07 UTC (permalink / raw)


On 5/5/20 1:46 PM, Simon Wright wrote:
> hreba <f_hreba@yahoo.com.br> writes:
> 
>> package body Aux is
>>     procedure Init (r: in out Rec) is
>>     begin
>>        r.p:= r.a'Access;  -- <-- error!
>>     end Init;
>> end Aux;
> 
> 'Unchecked_Access does the job (but this is outside my language-lawyering
> abilities!)
> 

Didn't expect that from you indeed. Besides "doing the job", what would 
be a clean solution (of a task that seems so basic to me)?

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 12:59   ` hreba
@ 2020-05-05 13:19     ` J-P. Rosen
  2020-05-05 13:37     ` Jere
  1 sibling, 0 replies; 39+ messages in thread
From: J-P. Rosen @ 2020-05-05 13:19 UTC (permalink / raw)


Le 05/05/2020 à 14:59, hreba a écrit :
> Don't understand that. R.a and R.p have exactly the same lifetime, which
> is the lifetime of R, haven't they?
> 
But R.a can be assigned to any global object, with unlimited lifetime.

Note that Unchecked_Access is not "dirty", it just reminds you that some
responsabilities of compiler checks are passed on to you. It tells you:
"beware with assignments".

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr

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

* Re: How can one record component be local and another not?
  2020-05-05 12:59   ` hreba
  2020-05-05 13:19     ` J-P. Rosen
@ 2020-05-05 13:37     ` Jere
  2020-05-05 14:28       ` hreba
  1 sibling, 1 reply; 39+ messages in thread
From: Jere @ 2020-05-05 13:37 UTC (permalink / raw)


On Tuesday, May 5, 2020 at 8:59:21 AM UTC-4, hreba wrote:
> On 5/5/20 1:33 PM, AdaMagica wrote:
> > Am Dienstag, 5. Mai 2020 13:04:56 UTC+2 schrieb hreba:
> >> I have reduced my problem to the following example:
> >>
> >> package Aux is
> >>      type Integer_P is access all Integer;
> >>      type Rec is record
> >>         a: aliased Integer;
> >>         p: Integer_P;
> >>      end record;
> >>
> >>      procedure Init (r: in out Rec);
> >> end Aux;
> >>
> >> package body Aux is
> >>      procedure Init (r: in out Rec) is
> >>      begin
> >>         r.p:= r.a'Access;  -- <-- error!
> >>      end Init;
> >> end Aux;
> > 
> > Imagine you define a local Rec:
> > 
> > with Aux;
> > procedure Proc is
> >    R: Aux.Rec;
> > begin
> >    Aux.Init (R);
> > end Proc;
> > 
> > Now R has shorter lifetime than the access type Integer_P. Thus you might have somewhere an access object AO with longer lifetime than R and try to assign
> > AO := R.P;
> > 
> > Try Unrestricted_Access.
> > 
> 
> Don't understand that. R.a and R.p have exactly the same lifetime, which 
> is the lifetime of R, haven't they?
> 

You are thinking about it in a way that looks at object lifetimes
only.  Ada often compares the lifetime of the access "type" to the lifetime
of the object (called accessibility levels in the RM).  I'm not 
sure why the decision to do it that way was made (I assume easier
implementation at the time).  I do know that some younger languages
have worked out some pretty useful schemes for tracking object lifetimes
along with the access variables.  Rust comes to mind and SPARK is also
coming along in that area as well.  Additionally there are some rules
concerning anonymous access types that allow a runtime checking of object
lifetimes (as opposed to static checking of Rust and soon SPARK), but
anonymous access types have a host of issues all their own and I don't
know them well enough to comment on how to do that reliably.

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

* Re: How can one record component be local and another not?
  2020-05-05 11:48 ` Niklas Holsti
@ 2020-05-05 13:44   ` hreba
  0 siblings, 0 replies; 39+ messages in thread
From: hreba @ 2020-05-05 13:44 UTC (permalink / raw)


On 5/5/20 1:48 PM, Niklas Holsti wrote:
> On 2020-05-05 14:04, hreba wrote:
>> I have reduced my problem to the following example:
>>
>> package Aux is
>>     type Integer_P is access all Integer;
>>     type Rec is record
>>        a: aliased Integer;
>>        p: Integer_P;
>>     end record;
>>
>>     procedure Init (r: in out Rec);
>> end Aux;
>>
>> package body Aux is
>>     procedure Init (r: in out Rec) is
>>     begin
>>        r.p:= r.a'Access;  -- <-- error!
>>     end Init;
>> end Aux;
>>
>> Compiling results in "non-local pointer cannot point to local object" 
>> indicating the marked line above.
>>
>> How can one record component be local and another not?
>>
>> Intention is to have a library subprogram Init() for initialization. 
>> How would I implement that instead?
>>
>> (The reason for the above construction is the need to pass a pointer 
>> to a C library function.)
>>
> 
> The parameter "r" might be passed by copy, in which case the r.a'Access 
> would point to the local copy, and become a dangling pointer on return 
> (copy-out).

Now I understand.

> It works if you replace "r: in out" with "r : access", which ensures 
> pass by reference.

Perfect, works indeed. Thanks a lot.

I tried another thing too: Changed Init into a constructor function 
returning r in an extended return statement. Didn't work. So even an 
extended return statement seems to create a local copy first.

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 13:37     ` Jere
@ 2020-05-05 14:28       ` hreba
  2020-05-05 15:18         ` AdaMagica
  0 siblings, 1 reply; 39+ messages in thread
From: hreba @ 2020-05-05 14:28 UTC (permalink / raw)


> You are thinking about it in a way that looks at object lifetimes
> only.  Ada often compares the lifetime of the access "type" to the lifetime
> of the object (called accessibility levels in the RM).

I really never got aware that types too have a lifetime (visibility yes, 
but not lifetime). Ok, I'll have to observe this in future.

Thanks for the explanation.

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 11:33 ` AdaMagica
  2020-05-05 11:38   ` AdaMagica
  2020-05-05 12:59   ` hreba
@ 2020-05-05 14:32   ` hreba
  2 siblings, 0 replies; 39+ messages in thread
From: hreba @ 2020-05-05 14:32 UTC (permalink / raw)


> Now R has shorter lifetime than the access type Integer_P.

At first read, I thought "access type Integer_P" was just a sloppy 
formulation for "variable of access type Integer_P". After learning from 
Jere (see below) that types too can have lifetimes, I understand.

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 14:28       ` hreba
@ 2020-05-05 15:18         ` AdaMagica
  0 siblings, 0 replies; 39+ messages in thread
From: AdaMagica @ 2020-05-05 15:18 UTC (permalink / raw)


Am Dienstag, 5. Mai 2020 16:28:26 UTC+2 schrieb hreba:
> I really never got aware that types too have a lifetime (visibility yes, 
> but not lifetime). Ok, I'll have to observe this in future.

Every declaration in Ada has a lifetime, except exceptions. These become anonymous if they leave the lifetime of their declaration.

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

* Re: How can one record component be local and another not?
  2020-05-05 11:04 How can one record component be local and another not? hreba
                   ` (3 preceding siblings ...)
  2020-05-05 11:48 ` Niklas Holsti
@ 2020-05-05 15:45 ` Jeffrey R. Carter
  2020-05-05 17:17   ` hreba
  2020-05-05 17:32 ` hreba
  5 siblings, 1 reply; 39+ messages in thread
From: Jeffrey R. Carter @ 2020-05-05 15:45 UTC (permalink / raw)


On 5/5/20 1:04 PM, hreba wrote:
> 
> (The reason for the above construction is the need to pass a pointer to a C 
> library function.)

Most likely you do not need to pass a pointer to your C function. For example, 
given the C function

void f (int* i);

You can do

procedure F (I : in out Interfaces.C.int) with Import, Convention => C, ...;

and the compiler will call the C function correctly.

-- 
Jeff Carter
"[T]he Musgroves had had the ill fortune
of a very troublesome, hopeless son, and
the good fortune to lose him before he
reached his twentieth year ..."
Persuasion
154

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

* Re: How can one record component be local and another not?
  2020-05-05 13:07   ` hreba
@ 2020-05-05 17:00     ` Dmitry A. Kazakov
  0 siblings, 0 replies; 39+ messages in thread
From: Dmitry A. Kazakov @ 2020-05-05 17:00 UTC (permalink / raw)


On 2020-05-05 15:07, hreba wrote:
> On 5/5/20 1:46 PM, Simon Wright wrote:
>> hreba <f_hreba@yahoo.com.br> writes:
>>
>>> package body Aux is
>>>     procedure Init (r: in out Rec) is
>>>     begin
>>>        r.p:= r.a'Access;  -- <-- error!
>>>     end Init;
>>> end Aux;
>>
>> 'Unchecked_Access does the job (but this is outside my language-lawyering
>> abilities!)
>>
> 
> Didn't expect that from you indeed. Besides "doing the job", what would 
> be a clean solution (of a task that seems so basic to me)?

There is no clean solution, because Rec is not limited or controlled.

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

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

* Re: How can one record component be local and another not?
  2020-05-05 15:45 ` Jeffrey R. Carter
@ 2020-05-05 17:17   ` hreba
  2020-05-05 19:08     ` Niklas Holsti
                       ` (2 more replies)
  0 siblings, 3 replies; 39+ messages in thread
From: hreba @ 2020-05-05 17:17 UTC (permalink / raw)


On 5/5/20 5:45 PM, Jeffrey R. Carter wrote:
> On 5/5/20 1:04 PM, hreba wrote:
>>
>> (The reason for the above construction is the need to pass a pointer 
>> to a C library function.)
> 
> Most likely you do not need to pass a pointer to your C function. For 
> example, given the C function
> 
> void f (int* i);
> 
> You can do
> 
> procedure F (I : in out Interfaces.C.int) with Import, Convention => C, 
> ...;
> 
> and the compiler will call the C function correctly.
> 

It is more complicated, unfortunately. I got

    type gsl_odeiv2_system is record
       c_function : access function
            (arg1 : double;
             arg2 : access double;
             arg3 : access double;
             arg4 : System.Address) return int;  -- 
/usr/include/gsl/gsl_odeiv2.h:58
       jacobian : access function
            (arg1 : double;
             arg2 : access double;
             arg3 : access double;
             arg4 : access double;
             arg5 : System.Address) return int;  -- 
/usr/include/gsl/gsl_odeiv2.h:60
       dimension : aliased size_t;  -- /usr/include/gsl/gsl_odeiv2.h:61
       params : System.Address;  -- /usr/include/gsl/gsl_odeiv2.h:62
    end record;
    pragma Convention (C_Pass_By_Copy, gsl_odeiv2_system);  -- 
/usr/include/gsl/gsl_odeiv2.h:64

and I have to pass a variable
    sys: access gsl_odeiv2_system
to the initialization function of that C library, and the

   params: System.Address;

component in the record, which internally is then passed to c_function() 
and jacobian(), posed the problem.

-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 11:04 How can one record component be local and another not? hreba
                   ` (4 preceding siblings ...)
  2020-05-05 15:45 ` Jeffrey R. Carter
@ 2020-05-05 17:32 ` hreba
  2020-05-05 19:04   ` Niklas Holsti
  2020-05-06 17:30   ` Niklas Holsti
  5 siblings, 2 replies; 39+ messages in thread
From: hreba @ 2020-05-05 17:32 UTC (permalink / raw)


Ok, with all your hints I came to the following solution:

--
package Aux is
    type Integer_P is access all Integer;
    type Rec is limited record
       a: aliased Integer;
       p: Integer_P;
    end record;

    procedure Init (r: access Rec);
end Aux;
--
package body Aux is
    procedure Init (r: access Rec) is
    begin
       r.p:= r.a'Access;
    end Init;
end Aux;
--
with Aux;
procedure Test is
    r:	aliased Aux.Rec;
begin
    Aux.Init (r'Access);
end Test;
--

It compiles nicely, and then:

frank@pc-frank:~/Temp/Test0$ ./test
raised PROGRAM_ERROR : aux.adb:4 accessibility check failed

#@!!0ßx*~@!!!
-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 17:32 ` hreba
@ 2020-05-05 19:04   ` Niklas Holsti
  2020-05-05 20:11     ` Niklas Holsti
  2020-05-06 17:30   ` Niklas Holsti
  1 sibling, 1 reply; 39+ messages in thread
From: Niklas Holsti @ 2020-05-05 19:04 UTC (permalink / raw)


On 2020-05-05 20:32, hreba wrote:
> Ok, with all your hints I came to the following solution:
> 
> -- 
> package Aux is
>     type Integer_P is access all Integer;
>     type Rec is limited record
>        a: aliased Integer;
>        p: Integer_P;
>     end record;
> 
>     procedure Init (r: access Rec);
> end Aux;
> -- 
> package body Aux is
>     procedure Init (r: access Rec) is
>     begin
>        r.p:= r.a'Access;
>     end Init;
> end Aux;
> -- 
> with Aux;
> procedure Test is
>     r:    aliased Aux.Rec;
> begin
>     Aux.Init (r'Access);
> end Test;
> -- 
> 
> It compiles nicely, and then:
> 
> frank@pc-frank:~/Temp/Test0$ ./test
> raised PROGRAM_ERROR : aux.adb:4 accessibility check failed
> 
> #@!!0ßx*~@!!!

Ok, apologies if I led you down the wrong path. I admit I did not build 
a running program to check my suggestion of the "access" parameter, and 
I have not myself often used this kind of code.

You should be able to get rid of this error by using 
r.a'Unchecked_Access instead of r.a'Access, and then (I think) you can 
return to using an "in out" parameter mode instead of "access" mode.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-05 17:17   ` hreba
@ 2020-05-05 19:08     ` Niklas Holsti
  2020-05-06 19:31       ` hreba
  2020-05-05 19:19     ` Jere
  2020-05-06  6:42     ` Mark Lorenzen
  2 siblings, 1 reply; 39+ messages in thread
From: Niklas Holsti @ 2020-05-05 19:08 UTC (permalink / raw)


On 2020-05-05 20:17, hreba wrote:
> On 5/5/20 5:45 PM, Jeffrey R. Carter wrote:
>> On 5/5/20 1:04 PM, hreba wrote:
>>>
>>> (The reason for the above construction is the need to pass a pointer 
>>> to a C library function.)
>>
>> Most likely you do not need to pass a pointer to your C function. For 
>> example, given the C function
>>
>> void f (int* i);
>>
>> You can do
>>
>> procedure F (I : in out Interfaces.C.int) with Import, Convention => 
>> C, ...;
>>
>> and the compiler will call the C function correctly.
>>
> 
> It is more complicated, unfortunately. I got
> 
>     type gsl_odeiv2_system is record
>        c_function : access function
>             (arg1 : double;
>              arg2 : access double;
>              arg3 : access double;
>              arg4 : System.Address) return int;  -- 
> /usr/include/gsl/gsl_odeiv2.h:58
>        jacobian : access function
>             (arg1 : double;
>              arg2 : access double;
>              arg3 : access double;
>              arg4 : access double;
>              arg5 : System.Address) return int;  -- 
> /usr/include/gsl/gsl_odeiv2.h:60
>        dimension : aliased size_t;  -- /usr/include/gsl/gsl_odeiv2.h:61
>        params : System.Address;  -- /usr/include/gsl/gsl_odeiv2.h:62
>     end record;
>     pragma Convention (C_Pass_By_Copy, gsl_odeiv2_system);  -- 
> /usr/include/gsl/gsl_odeiv2.h:64
> 
> and I have to pass a variable
>     sys: access gsl_odeiv2_system
> to the initialization function of that C library, and the
> 
>    params: System.Address;
> 
> component in the record, which internally is then passed to c_function() 
> and jacobian(), posed the problem.

I don't understand -- System.Address is not an access type; why do you 
need to use an access type to produce this parameter? Do you need a 
pointer to the "params" component?

Perhaps you can show a bit more of the actual code that has the problem?

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-05 17:17   ` hreba
  2020-05-05 19:08     ` Niklas Holsti
@ 2020-05-05 19:19     ` Jere
  2020-05-06  6:42     ` Mark Lorenzen
  2 siblings, 0 replies; 39+ messages in thread
From: Jere @ 2020-05-05 19:19 UTC (permalink / raw)


On Tuesday, May 5, 2020 at 1:17:42 PM UTC-4, hreba wrote:
> On 5/5/20 5:45 PM, Jeffrey R. Carter wrote:
> > On 5/5/20 1:04 PM, hreba wrote:
> >>
> >> (The reason for the above construction is the need to pass a pointer 
> >> to a C library function.)
> > 
> > Most likely you do not need to pass a pointer to your C function. For 
> > example, given the C function
> > 
> > void f (int* i);
> > 
> > You can do
> > 
> > procedure F (I : in out Interfaces.C.int) with Import, Convention => C, 
> > ...;
> > 
> > and the compiler will call the C function correctly.
> > 
> 
> It is more complicated, unfortunately. I got
> 
>     type gsl_odeiv2_system is record
>        c_function : access function
>             (arg1 : double;
>              arg2 : access double;
>              arg3 : access double;
>              arg4 : System.Address) return int;  -- 
> /usr/include/gsl/gsl_odeiv2.h:58
>        jacobian : access function
>             (arg1 : double;
>              arg2 : access double;
>              arg3 : access double;
>              arg4 : access double;
>              arg5 : System.Address) return int;  -- 
> /usr/include/gsl/gsl_odeiv2.h:60
>        dimension : aliased size_t;  -- /usr/include/gsl/gsl_odeiv2.h:61
>        params : System.Address;  -- /usr/include/gsl/gsl_odeiv2.h:62
>     end record;
>     pragma Convention (C_Pass_By_Copy, gsl_odeiv2_system);  -- 
> /usr/include/gsl/gsl_odeiv2.h:64
> 
> and I have to pass a variable
>     sys: access gsl_odeiv2_system
> to the initialization function of that C library, and the
> 
>    params: System.Address;
> 
> component in the record, which internally is then passed to c_function() 
> and jacobian(), posed the problem.
> 
> -- 
> Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

This isn't so much a question for you directly...maybe more for
those more experienced with the RM, but I was curious about the
implications of having an "aliased" parameter inside a record with 
a C convention specified.  For some reason, I thought that the 
aliased keyword implied a certain alignment in Ada (not necessarily 
in C) and didn't know if specifying the convention undid that 
alignment or if there is a risk of the record not always being
the same layout/alignment as the expected C struct for the aliased
parameter.

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

* Re: How can one record component be local and another not?
  2020-05-05 19:04   ` Niklas Holsti
@ 2020-05-05 20:11     ` Niklas Holsti
  2020-05-06 13:13       ` hreba
  0 siblings, 1 reply; 39+ messages in thread
From: Niklas Holsti @ 2020-05-05 20:11 UTC (permalink / raw)


On 2020-05-05 22:04, Niklas Holsti wrote:
> On 2020-05-05 20:32, hreba wrote:
>> Ok, with all your hints I came to the following solution:
>>
>> -- 
>> package Aux is
>>     type Integer_P is access all Integer;
>>     type Rec is limited record
>>        a: aliased Integer;
>>        p: Integer_P;
>>     end record;
>>
>>     procedure Init (r: access Rec);
>> end Aux;
>> -- 
>> package body Aux is
>>     procedure Init (r: access Rec) is
>>     begin
>>        r.p:= r.a'Access;
>>     end Init;
>> end Aux;
>> -- 
>> with Aux;
>> procedure Test is
>>     r:    aliased Aux.Rec;
>> begin
>>     Aux.Init (r'Access);
>> end Test;
>> -- 
>>
>> It compiles nicely, and then:
>>
>> frank@pc-frank:~/Temp/Test0$ ./test
>> raised PROGRAM_ERROR : aux.adb:4 accessibility check failed
>>
>> #@!!0ßx*~@!!!
> 
> Ok, apologies if I led you down the wrong path. I admit I did not build 
> a running program to check my suggestion of the "access" parameter, and 
> I have not myself often used this kind of code.
> 
> You should be able to get rid of this error by using 
> r.a'Unchecked_Access instead of r.a'Access, and then (I think) you can 
> return to using an "in out" parameter mode instead of "access" mode.

... but better make it "aliased in out" to ensure pass-by-reference.

I do agree with Dmitry that records with internal pointers-to-component 
should be "limited" or "controlled".

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-05 17:17   ` hreba
  2020-05-05 19:08     ` Niklas Holsti
  2020-05-05 19:19     ` Jere
@ 2020-05-06  6:42     ` Mark Lorenzen
  2020-05-06  8:26       ` Simon Wright
  2 siblings, 1 reply; 39+ messages in thread
From: Mark Lorenzen @ 2020-05-06  6:42 UTC (permalink / raw)


On Tuesday, May 5, 2020 at 7:17:42 PM UTC+2, hreba wrote:
> On 5/5/20 5:45 PM, Jeffrey R. Carter wrote:
> > On 5/5/20 1:04 PM, hreba wrote:
> >>
> >> (The reason for the above construction is the need to pass a pointer 
> >> to a C library function.)
> > 
> > Most likely you do not need to pass a pointer to your C function. For 
> > example, given the C function
> > 
> > void f (int* i);
> > 
> > You can do
> > 
> > procedure F (I : in out Interfaces.C.int) with Import, Convention => C, 
> > ...;
> > 
> > and the compiler will call the C function correctly.
> > 
> 
> It is more complicated, unfortunately. I got
> 
>     type gsl_odeiv2_system is record
>        c_function : access function
>             (arg1 : double;
>              arg2 : access double;
>              arg3 : access double;
>              arg4 : System.Address) return int;  -- 
> /usr/include/gsl/gsl_odeiv2.h:58
>        jacobian : access function
>             (arg1 : double;
>              arg2 : access double;
>              arg3 : access double;
>              arg4 : access double;
>              arg5 : System.Address) return int;  -- 
> /usr/include/gsl/gsl_odeiv2.h:60
>        dimension : aliased size_t;  -- /usr/include/gsl/gsl_odeiv2.h:61
>        params : System.Address;  -- /usr/include/gsl/gsl_odeiv2.h:62
>     end record;
>     pragma Convention (C_Pass_By_Copy, gsl_odeiv2_system);  -- 
> /usr/include/gsl/gsl_odeiv2.h:64
> 
> and I have to pass a variable
>     sys: access gsl_odeiv2_system
> to the initialization function of that C library, and the
> 
>    params: System.Address;
> 
> component in the record, which internally is then passed to c_function() 
> and jacobian(), posed the problem.
> 
> -- 
> Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

You need to step back and try to get an overview of what you are trying to achieve - not blindly copy some low-level C construct. Figure out why those C functions take parameters of an access type. Are they in/out or out parameters? Then define the Ada equivalent of those function access types e.g:

type C_Function_Access is access
   function (Arg1 : double; Arg2 : in out double; Arg3 : in out double; Arg4 : in System.Address) return int with Convention => C;

Note that in Ada 2012 functions are unfortunately allowed to have in/out-mode and out-mode parameters. In previous revisions, functions were only allowed to have in-mode parameters.

As explained by Jeffrey R. Carter above, Convention => C guarantees that in/out-mode and out-mode parameters are passed by value.

Your record type then becomes something like:

type GSL_Odeiv2_System is record
   C_Function : C_Function_Access;
   ...
end record
  with Convention C_Pass_By_Copy;

You are trying to create an Ada interface to C, but you are dragging the low-level nature of C into your Ada. When you encounter access types and addresses in Ada, then step back and try to get an overview of what needs to be achieved - not how it is done in C and thus mechanically copied in the auto-generated Ada code.

Regards,
Mark L

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

* Re: How can one record component be local and another not?
  2020-05-06  6:42     ` Mark Lorenzen
@ 2020-05-06  8:26       ` Simon Wright
  2020-05-06  8:33         ` Mark Lorenzen
  0 siblings, 1 reply; 39+ messages in thread
From: Simon Wright @ 2020-05-06  8:26 UTC (permalink / raw)


Mark Lorenzen <mark.lorenzen@gmail.com> writes:

> As explained by Jeffrey R. Carter above, Convention => C guarantees
> that in/out-mode and out-mode parameters are passed by value.

By address, surely?

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

* Re: How can one record component be local and another not?
  2020-05-06  8:26       ` Simon Wright
@ 2020-05-06  8:33         ` Mark Lorenzen
  0 siblings, 0 replies; 39+ messages in thread
From: Mark Lorenzen @ 2020-05-06  8:33 UTC (permalink / raw)


On Wednesday, May 6, 2020 at 10:26:14 AM UTC+2, Simon Wright wrote:
> Mark Lorenzen writes:
> 
> > As explained by Jeffrey R. Carter above, Convention => C guarantees
> > that in/out-mode and out-mode parameters are passed by value.
> 
> By address, surely?

Bummer! Yes - by address!

Regards,
Mark L

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

* Re: How can one record component be local and another not?
  2020-05-05 20:11     ` Niklas Holsti
@ 2020-05-06 13:13       ` hreba
  0 siblings, 0 replies; 39+ messages in thread
From: hreba @ 2020-05-06 13:13 UTC (permalink / raw)


On 5/5/20 10:11 PM, Niklas Holsti wrote:
> On 2020-05-05 22:04, Niklas Holsti wrote:
>> On 2020-05-05 20:32, hreba wrote:
>>> Ok, with all your hints I came to the following solution:
>>>
>>> -- 
>>> package Aux is
>>>     type Integer_P is access all Integer;
>>>     type Rec is limited record
>>>        a: aliased Integer;
>>>        p: Integer_P;
>>>     end record;
>>>
>>>     procedure Init (r: access Rec);
>>> end Aux;
>>> -- 
>>> package body Aux is
>>>     procedure Init (r: access Rec) is
>>>     begin
>>>        r.p:= r.a'Access;
>>>     end Init;
>>> end Aux;
>>> -- 
>>> with Aux;
>>> procedure Test is
>>>     r:    aliased Aux.Rec;
>>> begin
>>>     Aux.Init (r'Access);
>>> end Test;
>>> -- 
>>>
>>> It compiles nicely, and then:
>>>
>>> frank@pc-frank:~/Temp/Test0$ ./test
>>> raised PROGRAM_ERROR : aux.adb:4 accessibility check failed
>>>
>>> #@!!0ßx*~@!!!
>>
>> Ok, apologies if I led you down the wrong path. I admit I did not 
>> build a running program to check my suggestion of the "access" 
>> parameter, and I have not myself often used this kind of code.
>>
>> You should be able to get rid of this error by using 
>> r.a'Unchecked_Access instead of r.a'Access, and then (I think) you can 
>> return to using an "in out" parameter mode instead of "access" mode.
> 
> ... but better make it "aliased in out" to ensure pass-by-reference.

Perfect Niklas, all your proposed variants work!

> I do agree with Dmitry that records with internal pointers-to-component 
> should be "limited" or "controlled".

Did that already, as you can see above.

It was a recurring issue for me but only now I understood it and know 
how to solve it.

Thanks a lot for your help.
-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-05 17:32 ` hreba
  2020-05-05 19:04   ` Niklas Holsti
@ 2020-05-06 17:30   ` Niklas Holsti
  2020-05-06 18:28     ` Jere
  2020-05-07  9:07     ` J-P. Rosen
  1 sibling, 2 replies; 39+ messages in thread
From: Niklas Holsti @ 2020-05-06 17:30 UTC (permalink / raw)


On 2020-05-05 20:32, hreba wrote:
> Ok, with all your hints I came to the following solution:
> 
> -- 
> package Aux is
>     type Integer_P is access all Integer;
>     type Rec is limited record
>        a: aliased Integer;
>        p: Integer_P;
>     end record;
> 
>     procedure Init (r: access Rec);
> end Aux;
> -- 
> package body Aux is
>     procedure Init (r: access Rec) is
>     begin
>        r.p:= r.a'Access;
>     end Init;
> end Aux;
> -- 
> with Aux;
> procedure Test is
>     r:    aliased Aux.Rec;
> begin
>     Aux.Init (r'Access);
> end Test;
> -- 
> 
> It compiles nicely, and then:
> 
> frank@pc-frank:~/Temp/Test0$ ./test
> raised PROGRAM_ERROR : aux.adb:4 accessibility check failed
> 
> #@!!0ßx*~@!!!

Just to close out this issue, I wrote some test programs and explored 
various changes.

I believe the reason for this exception is that the accessibility level 
of the object "Test.r" is local, nested in the procedure "Test" (so "r" 
exists only for the relevant call of "Test") while the access type 
"Integer_P" is library-level and so out-lasts the call of "Test".

Note that, although "Test" here is no doubt the program's main 
subprogram and so its call (assuming it is not recursive) lasts almost 
as long as the execution of the program, the compiler does not make use 
of this fact to determine the life-times of types and objects in "Test".

If "r" is moved to a package, to be a statically allocated, 
library-level object, "Aux.Init (r'Access)" works without raising this 
exception.

Somewhat surprisingly to me, any call of the form
"Aux.Init (r'Unchecked_Access)" also works, whether "r" is a 
library-level object or a local (limited-life) object, even if 
"Aux.Init" itself uses "'Access" and not "'Unchecked_Access". Apparently 
the accessibility level provided in the value of Unchecked_Access is 
such as to make the check succeed, as for a library-level object.

I also found that a call like "Aux.Init (new Aux.Rec)", which applies 
"Aux.Init" to a heap-allocated "Rec" object, fails the accessibility 
check, so this check is really strict. Of course, the heap object can be 
deallocated at any time, so it may have a short life-time, but that can 
be done (mainly) by Unchecked_Deallocation, so it involves "unchecked" 
programming. Hm.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-06 17:30   ` Niklas Holsti
@ 2020-05-06 18:28     ` Jere
  2020-05-06 19:09       ` Niklas Holsti
  2020-05-07  9:07     ` J-P. Rosen
  1 sibling, 1 reply; 39+ messages in thread
From: Jere @ 2020-05-06 18:28 UTC (permalink / raw)


On Wednesday, May 6, 2020 at 1:30:58 PM UTC-4, Niklas Holsti wrote:
> On 2020-05-05 20:32, hreba wrote:
> > Ok, with all your hints I came to the following solution:
> > 
> > -- 
> > package Aux is
> >     type Integer_P is access all Integer;
> >     type Rec is limited record
> >        a: aliased Integer;
> >        p: Integer_P;
> >     end record;
> > 
> >     procedure Init (r: access Rec);
> > end Aux;
> > -- 
> > package body Aux is
> >     procedure Init (r: access Rec) is
> >     begin
> >        r.p:= r.a'Access;
> >     end Init;
> > end Aux;
> > -- 
> > with Aux;
> > procedure Test is
> >     r:    aliased Aux.Rec;
> > begin
> >     Aux.Init (r'Access);
> > end Test;
> > -- 
> > 
> <SNIPPED>
> Somewhat surprisingly to me, any call of the form
> "Aux.Init (r'Unchecked_Access)" also works, whether "r" is a 
> library-level object or a local (limited-life) object, even if 
> "Aux.Init" itself uses "'Access" and not "'Unchecked_Access". Apparently 
> the accessibility level provided in the value of Unchecked_Access is 
> such as to make the check succeed, as for a library-level object.
> 
Yeah, RM section 13.10 says it treats it as if the object were declared
at library level for Unchecked_Access

> I also found that a call like "Aux.Init (new Aux.Rec)", which applies 
> "Aux.Init" to a heap-allocated "Rec" object, fails the accessibility 
> check, so this check is really strict. Of course, the heap object can be 
> deallocated at any time, so it may have a short life-time, but that can 
> be done (mainly) by Unchecked_Deallocation, so it involves "unchecked" 
> programming. Hm.
> 
Is that because the heap allocated object is allocated using an
anonymous access type instead of a named access type per chance?
I know anonymous access types have different accessibility level
rules.  It might be setting the accessibility level to the point
of call maybe?


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

* Re: How can one record component be local and another not?
  2020-05-06 18:28     ` Jere
@ 2020-05-06 19:09       ` Niklas Holsti
  0 siblings, 0 replies; 39+ messages in thread
From: Niklas Holsti @ 2020-05-06 19:09 UTC (permalink / raw)


On 2020-05-06 21:28, Jere wrote:
> On Wednesday, May 6, 2020 at 1:30:58 PM UTC-4, Niklas Holsti wrote:
>> On 2020-05-05 20:32, hreba wrote:
>>> Ok, with all your hints I came to the following solution:
>>>
>>> -- 
>>> package Aux is
>>>      type Integer_P is access all Integer;
>>>      type Rec is limited record
>>>         a: aliased Integer;
>>>         p: Integer_P;
>>>      end record;
>>>
>>>      procedure Init (r: access Rec);
>>> end Aux;
>>> -- 
>>> package body Aux is
>>>      procedure Init (r: access Rec) is
>>>      begin
>>>         r.p:= r.a'Access;
>>>      end Init;
>>> end Aux;
>>> -- 
>>> with Aux;
>>> procedure Test is
>>>      r:    aliased Aux.Rec;
>>> begin
>>>      Aux.Init (r'Access);
>>> end Test;
>>> -- 
>>>
>> <SNIPPED>
>> Somewhat surprisingly to me, any call of the form
>> "Aux.Init (r'Unchecked_Access)" also works, whether "r" is a
>> library-level object or a local (limited-life) object, even if
>> "Aux.Init" itself uses "'Access" and not "'Unchecked_Access". Apparently
>> the accessibility level provided in the value of Unchecked_Access is
>> such as to make the check succeed, as for a library-level object.
>>
> Yeah, RM section 13.10 says it treats it as if the object were declared
> at library level for Unchecked_Access

That explains it clearly, thanks for the reference.

>> I also found that a call like "Aux.Init (new Aux.Rec)", which applies
>> "Aux.Init" to a heap-allocated "Rec" object, fails the accessibility
>> check, so this check is really strict. Of course, the heap object can be
>> deallocated at any time, so it may have a short life-time, but that can
>> be done (mainly) by Unchecked_Deallocation, so it involves "unchecked"
>> programming. Hm.
>>
> Is that because the heap allocated object is allocated using an
> anonymous access type instead of a named access type per chance?
> I know anonymous access types have different accessibility level
> rules.  It might be setting the accessibility level to the point
> of call maybe?

I made some more experiments:

a) anonymous access (Aux.Init (new Aux.Rec)): check fails

b) local named type "access Aux.Rec": check fails

c) library-level access type (access Aux.Rec declared in package Aux): 
works.

Interesting... I haven't developed a good intuition for this yet.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-05 19:08     ` Niklas Holsti
@ 2020-05-06 19:31       ` hreba
  2020-05-09 19:43         ` Niklas Holsti
  0 siblings, 1 reply; 39+ messages in thread
From: hreba @ 2020-05-06 19:31 UTC (permalink / raw)


On 5/5/20 9:08 PM, Niklas Holsti wrote:
> 
> I don't understand -- System.Address is not an access type; why do you 
> need to use an access type to produce this parameter? Do you need a 
> pointer to the "params" component?
> 
> Perhaps you can show a bit more of the actual code that has the problem?
> 

Ok, I'll try to make it comprehensible without throwing a lot of code at 
you.

I am writing a thick binding to the GSL library odeiv2 for numerically 
integrating ODEs (ordinary differential equations). I already have the 
thin binding gsl_odeiv2_h.ads. For initialization, I have to call the 
following subprogram of the thin binding:

    function gsl_odeiv2_driver_alloc_y_new
      (sys : access constant gsl_odeiv2_system;
       T : access constant gsl_odeiv2_step_type;
       hstart : double;
       epsabs : double;
       epsrel : double) return access gsl_odeiv2_driver;
    pragma Import (C, gsl_odeiv2_driver_alloc_y_new, 
"gsl_odeiv2_driver_alloc_y_new");

Parameter sys defines the ODE; its type is

    type gsl_odeiv2_system is record
       c_function : access function
            (arg1 : double;
             arg2 : access double;
             arg3 : access double;
             arg4 : System.Address) return int;
       jacobian : access function
            (arg1 : double;
             arg2 : access double;
             arg3 : access double;
             arg4 : access double;
             arg5 : System.Address) return int;
       dimension : aliased size_t;
       params : System.Address;
    end record;
    pragma Convention (C_Pass_By_Copy, gsl_odeiv2_system);

Later, during integration, c_function() and jacobian() will be called 
with parameters of type System.Address (arg4 and arg5 resp.). These 
parameters must be passed as params inside sys to 
gsl_odeiv2_driver_alloc_y_new().

Now to my own code (most of the thin binding was created automatically 
with g++ -c -fdump-ada-spec-slim). Dglsys_P corresponds to c_function in 
the thin binding, The package is essentially

generic
    dim:	Positive;
    type Float is digits<>;
package odeiv2 is

      err_noInit,	-- Init_Solve() has not been called
      err_Precision,	-- precision of C.double used in GSL not sufficient
      err_Method,	-- integration method not implemented
      err_SolveAgain:	-- Solve_Again() call without former Solve()
Exception;

    type Float_Array is array (1..dim) of Float;
    type Float_Matrix is array (1..dim, 1..dim) of Float;
    type Par_Array is array (Positive range <>) of Float;
    type Method is (RK8PD);
    type Solver is limited private;

    type Dglsys_P is not null access procedure
      (t:	Float;			-- independent variable
       y:	Float_Array;		-- dependent variables at t
       dydt:	out Float_Array;	-- 1. ordinary derivatives at t
       params:	Par_Array		-- any parameters
      );

    -- omitted: type Jacobi_P is access procedure...

    procedure Init_Solve
      -- Init. a dglsys, must be called once before calling
      -- (once or repeatedly) Solve() or Solve_Again().
      -- The resulting sol must be an access type because of the internal
      -- interface to the GSL library.
      (Dglsys:	Dglsys_P;		-- the system of diff. equ.
       Jacobi:	Jacobi_P;		-- the jacobi calc., if avail.
       pars:	access constant Par_Array;	-- the parameters
       met:	Method;			-- the solve method
       h_start:	Float;			-- initial step size
       epsabs:	Float;			-- absolute error tolerance
       epsrel:	Float;			-- relative error tolerance
       sol:	aliased in out Solver
      );

private

    package C renames Interfaces.C;
    package Ode renames gsl_odeiv2_h;

    type C_Array is array (Integer range <>) of aliased C.double;
    pragma Convention (C, C_Array);

    type Parameters is record
       dglpars:		access constant Par_Array;	-- pars of dglsys
       Dglsys:		Dglsys_P;			-- dglsys calc.
       Jacobi:		Jacobi_P;			-- Jacobi calc.
    end record;
    pragma Convention (C, Parameters);
    type Parameters_Ptr is access all Parameters;

    package Parameters_Conv is new
      System.Address_To_Access_Conversions (Parameters);

    type Solver is limited record
       initialized:	Boolean:= false;	-- Init_Solve() has been called
       firstcall_done:	Boolean;		-- first call done
       store_y:		Boolean;		-- copy y to ya
       ya:		C_Array (1..dim);	-- values of last Solve call
       sys:		aliased Ode.gsl_odeiv2_system;
       driver:		access Ode.gsl_odeiv2_driver;
       all_pars:		aliased Parameters;
       all_pp:		Parameters_Conv.Object_Pointer;
    end record;

end odeiv2;


In the package body I have to define the c_function above (thin binding 
spec.), which will call the corresponding function of type Dglsys_P 
(thick binding), and it finds this in the parameters. then I define the 
initialization subprogram.


package body odeiv2 is

    package C_Arrays is new
      Interfaces.C.Pointers (Integer, C.double, C_Array, 0.0);

    function func (t: C.double; y,f: access C.double; params: 
System.Address)	 return C.int;
    pragma Convention (C, func);

    function func (t: C.double; y,f: access C.double; params: 
System.Address)	 return C.int
      -- GSL version of Dglsys
    is
       use type System.Address;
       all_pars:	Parameters_Conv.Object_Pointer;
       yf, dydt:	Float_Array;
       y0:	C_Arrays.Pointer:= C_Arrays.Pointer(y);
       f0:	C_Arrays.Pointer:= C_Arrays.Pointer(f);
    begin
       all_pars:= Parameters_Conv.To_Pointer (params);
       for i in 1..dim loop
	 yf(i):= Float(y0.all);
	 C_Arrays.Increment (y0);
       end loop;
       all_pars.Dglsys (Float(t), yf, dydt, all_pars.dglpars.all);
       for i in 1..dim loop
	 f0.all:= C.double(dydt(i));
	 C_Arrays.Increment (f0);
       end loop;
       return gsl_errno_h.GSL_SUCCESS;
    end func;


    procedure Init_Solve
      (Dglsys:	Dglsys_P;		-- the system of diff.equ.
       Jacobi:	Jacobi_P;		-- jacobi calc., if available
       pars:	access constant Par_Array;	-- the parameters
       met:	Method;			-- the solve method
       h_start:	Float;			-- initial step size
       epsabs:	Float;			-- absolute error tolerance
       epsrel:	Float;			-- relative error tolerance
       sol:	aliased in out Solver
      )
    is
       gsl_met:	access constant Ode.gsl_odeiv2_step_type;
    begin
       -- set all_pars
       -- sol: flags for calling logic
       sol.Initialized:= true;
       sol.firstcall_done:= false;
       sol.store_y:= true;
       sol.all_pars.dglpars:= pars;
       sol.all_pars.Dglsys:= Dglsys;
       sol.all_pars.Jacobi:= Jacobi;
       -- transform integration method to GSL format
       case met is
	 when RK8PD =>	gsl_met:= Ode.gsl_odeiv2_step_rk8pd;
	 when others =>
	    raise err_Method
	      with "forgot to implement integration method";
       end case;
       -- build sys and driver
       sol.all_pp:= sol.all_pars'Unchecked_Access;
       sol.sys:=
	(func'Access, jac'Access, C.size_t(dim),
	 Parameters_Conv.To_Address (sol.all_pp));
       sol.driver:= Ode.gsl_odeiv2_driver_alloc_y_new
	(sol.sys'Access, gsl_met,
	 C.double(h_start), C.double(epsabs), C.double(epsrel));
       end Init_Solve;

    -- %< -------------
end odeiv2;


The above version compiles. At first I didn't have the Solver component 
all_pp, but passed app_pars'Address to the call of 
gsl_odeiv2_driver_alloc_y_new at the end of Init_Solve. It too compiled.

But whatever I try, I don't get rid of the runtime error

raised CONSTRAINT_ERROR : odeiv2.ads:92:4 access check failed

It accuses the line

    type Parameters is record


-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

* Re: How can one record component be local and another not?
  2020-05-06 17:30   ` Niklas Holsti
  2020-05-06 18:28     ` Jere
@ 2020-05-07  9:07     ` J-P. Rosen
  2020-05-07 10:15       ` Niklas Holsti
  2020-05-07 10:31       ` Stefan.Lucks
  1 sibling, 2 replies; 39+ messages in thread
From: J-P. Rosen @ 2020-05-07  9:07 UTC (permalink / raw)


Le 06/05/2020 à 19:30, Niklas Holsti a écrit :
> Note that, although "Test" here is no doubt the program's main
> subprogram and so its call (assuming it is not recursive) lasts almost
> as long as the execution of the program, the compiler does not make use
> of this fact to determine the life-times of types and objects in "Test".

No, tasks declared in library packages could survive the main program.
You may even have a "begin null; end" main program, and do all the work
with library tasks.

Moreover, there is no rule in Ada that makes the main subprogram "special".

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr

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

* Re: How can one record component be local and another not?
  2020-05-07  9:07     ` J-P. Rosen
@ 2020-05-07 10:15       ` Niklas Holsti
  2020-05-07 13:00         ` Egil H H
  2020-05-07 13:25         ` Simon Wright
  2020-05-07 10:31       ` Stefan.Lucks
  1 sibling, 2 replies; 39+ messages in thread
From: Niklas Holsti @ 2020-05-07 10:15 UTC (permalink / raw)


On 2020-05-07 12:07, J-P. Rosen wrote:
> Le 06/05/2020 à 19:30, Niklas Holsti a écrit :
>> Note that, although "Test" here is no doubt the program's main
>> subprogram and so its call (assuming it is not recursive) lasts almost
>> as long as the execution of the program, the compiler does not make use
>> of this fact to determine the life-times of types and objects in "Test".
> 
> No, tasks declared in library packages could survive the main program.

And the environment task, yes. I know, which is why I said "almost as 
long as". The example program we were discussing had no library tasks.

> You may even have a "begin null; end" main program, and do all the work
> with library tasks. 

Yes, but not for this example program. But I should have been clearer, I 
admit.

> Moreover, there is no rule in Ada that makes the main subprogram "special".

Indeed, I was trying to make that point, but evidently not clearly enough.

Sometimes this is a little annoying, where one wants to write a small 
program with some library-level stuff, but must then separate those 
declarations into a package. An option to have a "main package" instead 
of a "main subprogram" would sometimes be neat (and now somebody will 
tell me that Gnat already has that...)

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-07  9:07     ` J-P. Rosen
  2020-05-07 10:15       ` Niklas Holsti
@ 2020-05-07 10:31       ` Stefan.Lucks
  2020-05-07 11:58         ` J-P. Rosen
  1 sibling, 1 reply; 39+ messages in thread
From: Stefan.Lucks @ 2020-05-07 10:31 UTC (permalink / raw)


[-- Attachment #1: Type: text/plain, Size: 1078 bytes --]

On Thu, 7 May 2020, J-P. Rosen wrote:

> No, tasks declared in library packages could survive the main program.

Correct me, if I am wrong, but my understanding has always been that the 
main program (or "environment task") can only terminate after all the 
tasks started within the main program have terminated (or, perhaps, have 
been aborted, or did run into an unhandled exception).

> You may even have a "begin null; end" main program, and do all the work
> with library tasks.

Once again, my understanding is that a main program with the "begin null; 
end Program_Name" does not terminate, as long as one of its tasks is still 
running. In fact, I am frequently using the "begin null; end 
Program_Name;" pattern myself, and "Program_Name" does not terminate, as 
long as any of its tasks is still alife and kicking.


--------  I  love  the  taste  of  Cryptanalysis  in  the morning!  --------
www.uni-weimar.de/de/medien/professuren/mediensicherheit/people/stefan-lucks
----Stefan.Lucks (at) uni-weimar.de, Bauhaus-Universität Weimar, Germany----

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

* Re: How can one record component be local and another not?
  2020-05-07 10:31       ` Stefan.Lucks
@ 2020-05-07 11:58         ` J-P. Rosen
  0 siblings, 0 replies; 39+ messages in thread
From: J-P. Rosen @ 2020-05-07 11:58 UTC (permalink / raw)


Le 07/05/2020 à 12:31, Stefan.Lucks@uni-weimar.de a écrit :
> On Thu, 7 May 2020, J-P. Rosen wrote:
> 
>> No, tasks declared in library packages could survive the main program.
> 
> Correct me, if I am wrong, but my understanding has always been that the
> main program (or "environment task") can only terminate after all the
> tasks started within the main program have terminated (or, perhaps, have
> been aborted, or did run into an unhandled exception).
True AND False. The master of a library task is the environment task,
not the procedure declared as the main program. So you can return from
the main procedure, and then wait for the termination of library tasks.

The confusion is that the procedure declared as "main" is not the real
main program. The real main program is generated by the compiler, it
elaborates the library packages, and then calls the main procedure. In
gnat, you can even see the source (file b__*.adb)

>> You may even have a "begin null; end" main program, and do all the work
>> with library tasks.
> 
> Once again, my understanding is that a main program with the "begin
> null; end Program_Name" does not terminate, as long as one of its tasks
> is still running. 
As explained above, Program_Name terminates, but not the "real" main
program that called it.

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr

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

* Re: How can one record component be local and another not?
  2020-05-07 10:15       ` Niklas Holsti
@ 2020-05-07 13:00         ` Egil H H
  2020-05-07 13:25         ` Simon Wright
  1 sibling, 0 replies; 39+ messages in thread
From: Egil H H @ 2020-05-07 13:00 UTC (permalink / raw)


On Thursday, May 7, 2020 at 12:15:07 PM UTC+2, Niklas Holsti wrote:
> On 2020-05-07 12:07, J-P. Rosen wrote:
> > Le 06/05/2020 à 19:30, Niklas Holsti a écrit :
> >> Note that, although "Test" here is no doubt the program's main
> >> subprogram and so its call (assuming it is not recursive) lasts almost
> >> as long as the execution of the program, the compiler does not make use
> >> of this fact to determine the life-times of types and objects in "Test".
> > 
> > No, tasks declared in library packages could survive the main program.
> 
> And the environment task, yes. I know, which is why I said "almost as 
> long as". The example program we were discussing had no library tasks.
> 
> > You may even have a "begin null; end" main program, and do all the work
> > with library tasks. 
> 
> Yes, but not for this example program. But I should have been clearer, I 
> admit.
> 
> > Moreover, there is no rule in Ada that makes the main subprogram "special".
> 
> Indeed, I was trying to make that point, but evidently not clearly enough.
> 
> Sometimes this is a little annoying, where one wants to write a small 
> program with some library-level stuff, but must then separate those 
> declarations into a package. An option to have a "main package" instead 
> of a "main subprogram" would sometimes be neat (and now somebody will 
> tell me that Gnat already has that...)
> 
> -- 
> Niklas Holsti
> Tidorum Ltd
> niklas holsti tidorum fi
>        .      @       .

Since you asked so nicely...

Sort of, 

-z
No main subprogram. Bind and link the program even if the unit name given on the command line is a package name. The resulting executable will execute the elaboration routines of the package and its closure, then the finalization routines.


I believe that's needed for the DSA?

-- 
~egilhh

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

* Re: How can one record component be local and another not?
  2020-05-07 10:15       ` Niklas Holsti
  2020-05-07 13:00         ` Egil H H
@ 2020-05-07 13:25         ` Simon Wright
  1 sibling, 0 replies; 39+ messages in thread
From: Simon Wright @ 2020-05-07 13:25 UTC (permalink / raw)


Niklas Holsti <niklas.holsti@tidorum.invalid> writes:

>> No, tasks declared in library packages could survive the main program.
>
> And the environment task, yes.

I've seen people suggest aborting the environment task as a way of
ending a program with library tasks .. I suppose the main program could
exit without terminating the environment task .. looking at ARM 10.2,
(25) says "When the environment task completes (normally or abnormally),
it waits for the termination of all such tasks, and then finalizes any
remaining objects of the partition." but (30) says "If the environment
task completes abnormally, the implementation may abort any dependent
tasks.".

Cortex GNAT RTS treats exiting the main program as the same as exiting
any other taks body, i.e. illegal under Ravenscar, but I suppose I could
include 'delay until Ada.Real_Time.Time_Last;' - might result in fewer
surprises and would be more ARM-compliant.

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

* Re: How can one record component be local and another not?
  2020-05-06 19:31       ` hreba
@ 2020-05-09 19:43         ` Niklas Holsti
  2020-05-10 15:10           ` hreba
  0 siblings, 1 reply; 39+ messages in thread
From: Niklas Holsti @ 2020-05-09 19:43 UTC (permalink / raw)


On 2020-05-06 22:31, hreba wrote:
> On 5/5/20 9:08 PM, Niklas Holsti wrote:
>>
>> I don't understand -- System.Address is not an access type; why do you 
>> need to use an access type to produce this parameter? Do you need a 
>> pointer to the "params" component?
>>
>> Perhaps you can show a bit more of the actual code that has the problem?
>>
> 
> Ok, I'll try to make it comprehensible without throwing a lot of code at 
> you.

Thanks, and apologies for a late response (and this isn't a full 
response yet).

    [snip]

>        sol.all_pp:= sol.all_pars'Unchecked_Access;

    [snip]

> The above version compiles. At first I didn't have the Solver component 
> all_pp, but passed app_pars'Address to the call of 
> gsl_odeiv2_driver_alloc_y_new at the end of Init_Solve. It too compiled.
> 
> But whatever I try, I don't get rid of the runtime error
> 
> raised CONSTRAINT_ERROR : odeiv2.ads:92:4 access check failed

Do you mean that the exception occurs even with Unchecked_Access (as in 
the code you displayed) instead of plain Access?

> It accuses the line
> 
>     type Parameters is record

If the exception still happens with Unchecked_Access, can you get a full 
trace-back to show where the accessibility check is invoked?

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

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

* Re: How can one record component be local and another not?
  2020-05-09 19:43         ` Niklas Holsti
@ 2020-05-10 15:10           ` hreba
  0 siblings, 0 replies; 39+ messages in thread
From: hreba @ 2020-05-10 15:10 UTC (permalink / raw)


Hi Niklas,

Problem resolved: One of the components of Parameters is a not null 
access type, and I didn't initialize the instance right at the 
declaration, so for a short time the component was null.

I localized the error with -gnatD, which I found only now and only by 
chance.

Thanks again for your helpfulness.
-- 
Frank Hrebabetzky, Kronach	+49 / 9261 / 950 0565

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

end of thread, other threads:[~2020-05-10 15:10 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-05 11:04 How can one record component be local and another not? hreba
2020-05-05 11:33 ` AdaMagica
2020-05-05 11:38   ` AdaMagica
2020-05-05 12:59   ` hreba
2020-05-05 13:19     ` J-P. Rosen
2020-05-05 13:37     ` Jere
2020-05-05 14:28       ` hreba
2020-05-05 15:18         ` AdaMagica
2020-05-05 14:32   ` hreba
2020-05-05 11:43 ` AdaMagica
2020-05-05 12:55   ` hreba
2020-05-05 11:46 ` Simon Wright
2020-05-05 13:07   ` hreba
2020-05-05 17:00     ` Dmitry A. Kazakov
2020-05-05 11:48 ` Niklas Holsti
2020-05-05 13:44   ` hreba
2020-05-05 15:45 ` Jeffrey R. Carter
2020-05-05 17:17   ` hreba
2020-05-05 19:08     ` Niklas Holsti
2020-05-06 19:31       ` hreba
2020-05-09 19:43         ` Niklas Holsti
2020-05-10 15:10           ` hreba
2020-05-05 19:19     ` Jere
2020-05-06  6:42     ` Mark Lorenzen
2020-05-06  8:26       ` Simon Wright
2020-05-06  8:33         ` Mark Lorenzen
2020-05-05 17:32 ` hreba
2020-05-05 19:04   ` Niklas Holsti
2020-05-05 20:11     ` Niklas Holsti
2020-05-06 13:13       ` hreba
2020-05-06 17:30   ` Niklas Holsti
2020-05-06 18:28     ` Jere
2020-05-06 19:09       ` Niklas Holsti
2020-05-07  9:07     ` J-P. Rosen
2020-05-07 10:15       ` Niklas Holsti
2020-05-07 13:00         ` Egil H H
2020-05-07 13:25         ` Simon Wright
2020-05-07 10:31       ` Stefan.Lucks
2020-05-07 11:58         ` J-P. Rosen

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