comp.lang.ada
 help / color / mirror / Atom feed
* How to Emulate C++ Macro with Static Local Variable?
@ 2003-09-24 22:06 taashlo
  2003-09-24 23:44 ` Robert I. Eachus
                   ` (2 more replies)
  0 siblings, 3 replies; 28+ messages in thread
From: taashlo @ 2003-09-24 22:06 UTC (permalink / raw)


Hello,

I'm trying to port a small C++ library to Ada95.  Most of it is going
quite well in that the Ada implementation is smaller, simpler, and
easier to follow than the C++ implementation.  But I've ran into a
hurdle with the following C++ construction:

class Test {
...
protected:
   struct s {
      int data[8];
   };
...
   void bar(s *t, int foo);
   #define BAR(foo_) if (1) { \
      static s t_; \
      bar(&t_, foo_);\
   } else ((void)0)
...
};

What happens is that when you do this:

BAR(10);

it expands to this:

if (1) {
  static s t_;
  bar(&t_, 10);
} else ((void)0);

This, in effect, creates a (hidden) static local variable for each call
to bar().  This static variable (actually a structure, t_) is used to
cache information for each unique call to bar() such that subsequent
calls from that location can be processed *much* faster.

For instance, the following code would create three of the hidden local
variables:

for (int i = 0; i < 55; ++i) {
   switch (wozit()) {
      case 1: BAR(10); break;
      case 2: BAR(20); break;
      case 3: BAR(30); break;
   }
}

The first time each BAR() is encountered, it does the full processing,
but subsequent calls to the *same* BAR() used it's unique cached
information and go much faster.

Any ideas on how I can achieve this with Ada95?

Thank you,

Tad



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-24 22:06 How to Emulate C++ Macro with Static Local Variable? taashlo
@ 2003-09-24 23:44 ` Robert I. Eachus
  2003-09-25  3:27   ` Hyman Rosen
  2003-09-25 12:59   ` Tad Ashlock
  2003-09-25 11:54 ` Gautier
  2003-09-25 13:41 ` Stephen Leake
  2 siblings, 2 replies; 28+ messages in thread
From: Robert I. Eachus @ 2003-09-24 23:44 UTC (permalink / raw)


taashlo@sandia.gov wrote:
> Hello,
> 
> I'm trying to port a small C++ library to Ada95.  Most of it is going
> quite well in that the Ada implementation is smaller, simpler, and
> easier to follow than the C++ implementation.  But I've ran into a
> hurdle with the following C++ construction:
> 
> class Test {
> ...
> protected:
>    struct s {
>       int data[8];
>    };
> ...
>    void bar(s *t, int foo);
>    #define BAR(foo_) if (1) { \
>       static s t_; \
>       bar(&t_, foo_);\
>    } else ((void)0)
> ...
> };
> 
> What happens is that when you do this:
> 
> BAR(10);
> 
> it expands to this:
> 
> if (1) {
>   static s t_;
>   bar(&t_, 10);
> } else ((void)0);
> 
> This, in effect, creates a (hidden) static local variable for each call
> to bar().  This static variable (actually a structure, t_) is used to
> cache information for each unique call to bar() such that subsequent
> calls from that location can be processed *much* faster.
> 
> For instance, the following code would create three of the hidden local
> variables:
> 
> for (int i = 0; i < 55; ++i) {
>    switch (wozit()) {
>       case 1: BAR(10); break;
>       case 2: BAR(20); break;
>       case 3: BAR(30); break;
>    }
> }
> 
> The first time each BAR() is encountered, it does the full processing,
> but subsequent calls to the *same* BAR() used it's unique cached
> information and go much faster.
> 
> Any ideas on how I can achieve this with Ada95?

Wild, I'm going to give you "ideas," and let you implement one of them.

First, the normal way to implement this in Ada would be to break the 
action into a generic instantiation followed by a call of the 
instantiated generic.  But if you want to follow that pattern here it 
gets tricky to wrap it in a generic.  You would need a subprogram access 
type declared at an outer level, the generic instantiation would create 
a procedure and pass back the access value for subsequent calls.

The fast way to implement this of course is to use self-modifying code. 
  Ugh!  You can manage this with subprogram parameters.  The first call 
goes to the 'generic' version which returns among other things the new 
address to call.  Sort of:

procedure Subp is

   type Subprogram_Record;

   type Subprogram_Pointer is
        access procedure(X: in Integer; P: in out Subprogram_Record);

   type Subprogram_Record is record
     SP: Subprogram_Pointer;
   end record;

   procedure Test (X: in Integer; P: in out Subprogram_Record)
   is begin null; end;

   Bar_Pointer: constant Subprogram_Record := (SP => Test'Access);

   Local_Pointer: Subprogram_Record := Bar_Pointer;
begin
   Local_Pointer.SP.all(3, Local_Pointer);
end Subp;

This code is a bit ugly, but it does compile.  And since Bar_Pointer is 
a constant, you can't accidently pass it to Test.  There might be a 
cleaner cleverer way to do it, but this works.

The cleanest option may be to run your code through a preprocessor and 
use a similar macro.

-- 
                                                        Robert I. Eachus

Ryan gunned down the last of his third white wine and told himself it 
would all be over in a
few minutes.  One thing he'd learned from Operation Beatrix: This field 
work wasn't for him. --from Red Rabbit by Tom Clancy.




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-24 23:44 ` Robert I. Eachus
@ 2003-09-25  3:27   ` Hyman Rosen
  2003-09-25  4:53     ` tmoran
  2003-09-25 13:43     ` Stephen Leake
  2003-09-25 12:59   ` Tad Ashlock
  1 sibling, 2 replies; 28+ messages in thread
From: Hyman Rosen @ 2003-09-25  3:27 UTC (permalink / raw)


Robert I. Eachus wrote:
> This code is a bit ugly, but it does compile.  And since Bar_Pointer is 
> a constant, you can't accidently pass it to Test.  There might be a 
> cleaner cleverer way to do it, but this works.
> 
> The cleanest option may be to run your code through a preprocessor and 
> use a similar macro.

Robert, I'm almost ashamed of you! You're abandoning your Ada ways
and starting to think like a C++ hacker! :-)

The whole design is wrong in the C++ to begin with. If you want to have
a memoizing function, the job of caching old values belongs to the function
itself, not scattered in macros and variables all over the place.

In Ada, I suppose that I would just stick bar(foo:integer) into a package,
then have one of the Charles maps in the same package to hold memoized values.
There are lots of variations if allocation is forbidden, or if memoization
must be controlled from the call site, but none of them should require
sprinkling statics all over the place.




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25  3:27   ` Hyman Rosen
@ 2003-09-25  4:53     ` tmoran
  2003-09-25 15:57       ` Robert I. Eachus
  2003-09-25 13:43     ` Stephen Leake
  1 sibling, 1 reply; 28+ messages in thread
From: tmoran @ 2003-09-25  4:53 UTC (permalink / raw)


>The whole design is wrong in the C++ to begin with. If you want to have
>a memoizing function, the job of caching old values belongs to the function
>itself, not scattered in macros and variables all over the place.
  I quite agree.  And I don't understand how the "procedure Subp" is
supposed to be used.
  The OP does raise an interesting question, though.  Essentially he wants
to create an array indexed by particular ranges of the Instruction Counter,
and he doesn't want the trouble or error-proneness of having to include
specific marker code (eg, Where_I_Am := 27; or Saved_Info(72) :=...)
type code to mark different areas with unique numbers.  I don't see how
one might do that without a preprocessor, and I don't see how one could
(portably) get hold of the Instruction Counter for storage and later
comparison, in Ada code.
In the presence of tasking of course, it would be disaster waiting to
happen anyway.



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-24 22:06 How to Emulate C++ Macro with Static Local Variable? taashlo
  2003-09-24 23:44 ` Robert I. Eachus
@ 2003-09-25 11:54 ` Gautier
  2003-09-25 16:14   ` Robert I. Eachus
  2003-09-25 18:06   ` Tad Ashlock
  2003-09-25 13:41 ` Stephen Leake
  2 siblings, 2 replies; 28+ messages in thread
From: Gautier @ 2003-09-25 11:54 UTC (permalink / raw)


Something like that:

with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Test_Static_in_generic is

  type S is array(0..7) of Integer;

  generic
    foo: Integer;
  package P is
    procedure BAR;
  end;
  
  package body P is
    initialized: Boolean:= False;
    cache: S;
    procedure BAR is
    begin
      if not initialized then
        cache:= (others=> foo);
        initialized:= True;
      end if;
      Put(cache(cache'last));
    end;
  end;

  package P10 is new P(10);
  package P20 is new P(20);
  package P30 is new P(30);

begin
  for count in 1..4 loop
    for i in 1..3 loop
      case i is
        when 1=> P10.BAR;
        when 2=> P20.BAR;
        when 3=> P30.BAR;
      end case;
    end loop;
    New_Line;
  end loop;
end;

?

BTW: what this solution with pointers is about ?!
________________________________________________________
Gautier  --  http://www.mysunrise.ch/users/gdm/gsoft.htm

NB: For a direct answer, e-mail address on the Web site!



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-24 23:44 ` Robert I. Eachus
  2003-09-25  3:27   ` Hyman Rosen
@ 2003-09-25 12:59   ` Tad Ashlock
  2003-09-25 16:11     ` Robert I. Eachus
  1 sibling, 1 reply; 28+ messages in thread
From: Tad Ashlock @ 2003-09-25 12:59 UTC (permalink / raw)


"Robert I. Eachus" <rieachus@attbi.com> wrote in message news:<3F722C53.9020808@attbi.com>...
> taashlo@sandia.gov wrote:
> > Hello,
> > 
> > I'm trying to port a small C++ library to Ada95.  Most of it is going
> > quite well in that the Ada implementation is smaller, simpler, and
> > easier to follow than the C++ implementation.  But I've ran into a
> > hurdle with the following C++ construction:
> > 
> > class Test {
> > ...
> > protected:
> >    struct s {
> >       int data[8];
> >    };
> > ...
> >    void bar(s *t, int foo);
> >    #define BAR(foo_) if (1) { \
> >       static s t_; \
> >       bar(&t_, foo_);\
> >    } else ((void)0)
>  ...
> > };
> > 
> > What happens is that when you do this:
> > 
> > BAR(10);
> > 
> > it expands to this:
> > 
> > if (1) {
> >   static s t_;
> >   bar(&t_, 10);
> > } else ((void)0);
> > 
> > This, in effect, creates a (hidden) static local variable for each call
> > to bar().  This static variable (actually a structure, t_) is used to
> > cache information for each unique call to bar() such that subsequent
> > calls from that location can be processed *much* faster.
> > 
> > For instance, the following code would create three of the hidden local
> > variables:
> > 
> > for (int i = 0; i < 55; ++i) {
> >    switch (wozit()) {
> >       case 1: BAR(10); break;
> >       case 2: BAR(20); break;
> >       case 3: BAR(30); break;
> >    }
> > }
> > 
> > The first time each BAR() is encountered, it does the full processing,
> > but subsequent calls to the *same* BAR() used it's unique cached
> > information and go much faster.
> > 
> > Any ideas on how I can achieve this with Ada95?
> 
> Wild, I'm going to give you "ideas," and let you implement one of them.
> 
> First, the normal way to implement this in Ada would be to break the 
> action into a generic instantiation followed by a call of the 
> instantiated generic.  But if you want to follow that pattern here it 
> gets tricky to wrap it in a generic.  You would need a subprogram access 
> type declared at an outer level, the generic instantiation would create 
> a procedure and pass back the access value for subsequent calls.

I've been trying ideas along these lines for a few days, but I haven't
been able to make them work.  I understand how the procedure would get
created, but I don't understand where the data is uniquely cached for
each procedure call. (The C++ implementation above does it in t_->data.)

> The fast way to implement this of course is to use self-modifying code. 
>   Ugh!  You can manage this with subprogram parameters.  The first call 
> goes to the 'generic' version which returns among other things the new 
> address to call.  Sort of:
> 
> procedure Subp is
> 
>    type Subprogram_Record;
> 
>    type Subprogram_Pointer is
>         access procedure(X: in Integer; P: in out Subprogram_Record);
> 
>    type Subprogram_Record is record
>      SP: Subprogram_Pointer;
>    end record;
> 
>    procedure Test (X: in Integer; P: in out Subprogram_Record)
>    is begin null; end;
> 
>    Bar_Pointer: constant Subprogram_Record := (SP => Test'Access);
> 
>    Local_Pointer: Subprogram_Record := Bar_Pointer;
> begin
>    Local_Pointer.SP.all(3, Local_Pointer);
> end Subp;

I'm not following this one at all.  Where is the cached data?

> This code is a bit ugly, but it does compile.  And since Bar_Pointer is 
> a constant, you can't accidently pass it to Test.  There might be a 
> cleaner cleverer way to do it, but this works.
> 
> The cleanest option may be to run your code through a preprocessor and 
> use a similar macro.

Yeah, I've thought of that one too.  And due to performance requirements,
it might end up being my only option.  But even that isn't as straight
forward as in C++ due to the fact that the data cache needs to be
declared in the *package's* declarative region with a unique name.
(Where the C++ implementation could declare the data cache statically in
a block immediately before the function call.)

Thank you so much, Robert, for taking the time to reply to my question.

Tad



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-24 22:06 How to Emulate C++ Macro with Static Local Variable? taashlo
  2003-09-24 23:44 ` Robert I. Eachus
  2003-09-25 11:54 ` Gautier
@ 2003-09-25 13:41 ` Stephen Leake
  2003-09-25 17:23   ` Tad Ashlock
  2 siblings, 1 reply; 28+ messages in thread
From: Stephen Leake @ 2003-09-25 13:41 UTC (permalink / raw)


taashlo@sandia.gov writes:

> What happens is that when you do this:
> 
> BAR(10);
> 
> it expands to this:
> 
> if (1) {
>   static s t_;
>   bar(&t_, 10);
> } else ((void)0);
> 
> This, in effect, creates a (hidden) static local variable for each call
> to bar().  This static variable (actually a structure, t_) is used to
> cache information for each unique call to bar() such that subsequent
> calls from that location can be processed *much* faster.

Just declare each "hidden static" variable directly, and call 'bar'. 

Yes, it's a few more lines of code, but it's worth it!

> for (int i = 0; i < 55; ++i) {
>    switch (wozit()) {
>       case 1: BAR(10); break;
>       case 2: BAR(20); break;
>       case 3: BAR(30); break;
>    }
> }

This would become:

package foo
    t_1 : s;
    t_2 : s;
    t_3 : s;

procedure Something is
begin
    for i in 1 .. 54 loop
       case wozit is
       when 1 =>
           bar (t_1, 10);
       when 2 =>
           bar (t_2, 20);
       when 3 =>
           bar (t_3, 30);
       end case;
    end loop;
end Something;

end foo;

You may complain this is using "ugly global variables", and it is. So is the
C code. Using macros to hide doesn't really make it prettier :).

A better way is to make 't' part of a state object that is passed in
to Something, but that's left as an exercise for the reader.

-- 
-- Stephe



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25  3:27   ` Hyman Rosen
  2003-09-25  4:53     ` tmoran
@ 2003-09-25 13:43     ` Stephen Leake
  1 sibling, 0 replies; 28+ messages in thread
From: Stephen Leake @ 2003-09-25 13:43 UTC (permalink / raw)


Hyman Rosen <hyrosen@mail.com> writes:

> Robert I. Eachus wrote:
> > This code is a bit ugly, but it does compile.  And since Bar_Pointer
> > is a constant, you can't accidently pass it to Test.  There might be
> > a cleaner cleverer way to do it, but this works.
> > The cleanest option may be to run your code through a preprocessor
> > and use a similar macro.
> 
> Robert, I'm almost ashamed of you! You're abandoning your Ada ways
> and starting to think like a C++ hacker! :-)

AMEN!

> The whole design is wrong in the C++ to begin with. If you want to
> have a memoizing function, the job of caching old values belongs to
> the function itself, not scattered in macros and variables all over
> the place.

Yes, that is probably the best way. Although it may be that the state
should be preserved at a higher level; depends on the details.

> In Ada, I suppose that I would just stick bar(foo:integer) into a
> package, then have one of the Charles maps in the same package to
> hold memoized values. 

The C code did not dynamically allocate memo state, but that is
certainly a reasonable improvement.

-- 
-- Stephe



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25  4:53     ` tmoran
@ 2003-09-25 15:57       ` Robert I. Eachus
  2003-09-25 19:09         ` tmoran
  0 siblings, 1 reply; 28+ messages in thread
From: Robert I. Eachus @ 2003-09-25 15:57 UTC (permalink / raw)


tmoran@acm.org wrote:

 > I don't see how
> one might do that without a preprocessor, and I don't see how one could
> (portably) get hold of the Instruction Counter for storage and later
> comparison, in Ada code.

I showed how.  You set it up so that the memoizing function is not 
visible.  All that is visible is a constant record containing a 
subprogram access value.  To make a call you have to make a (variable) 
copy of the constant, and pass that on the call.  Inside the subprogram 
   this is a new call site.  Create a new access value, reset the 
(record) parameter to the new value, do the initial call processing, 
then call the (hidden) value which will be returned.

This second value of the parameter calls the routine which does the 
actual processing sans the initial call code.  This way there is no 
lookup or anything to be done in either case.  Call the initial value in 
the constant, this is the first call from this site.  Call the returned 
value, and this is a subsequent call.  If you want you can either make 
sure that the record containing the access value is passed by reference, 
or add an access discriminant so that inside the first subprogram you 
can cache a list of call sites if you want to.

-- 
                                                        Robert I. Eachus

Ryan gunned down the last of his third white wine and told himself it 
would all be over in a few minutes.  One thing he'd learned from 
Operation Beatrix: This field work wasn't for him. --from Red Rabbit by 
Tom Clancy.




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 12:59   ` Tad Ashlock
@ 2003-09-25 16:11     ` Robert I. Eachus
  2003-09-25 18:13       ` Randy Brukardt
  0 siblings, 1 reply; 28+ messages in thread
From: Robert I. Eachus @ 2003-09-25 16:11 UTC (permalink / raw)


Tad Ashlock wrote:

> I've been trying ideas along these lines for a few days, but I haven't
> been able to make them work.  I understand how the procedure would get
> created, but I don't understand where the data is uniquely cached for
> each procedure call. (The C++ implementation above does it in t_->data.)

>>   type Subprogram_Record is record
>>     SP: Subprogram_Pointer;
-- Put whatever else you want right here. Just like in the C++ there has 
  -- to be a local modifiable object of this type to make the call.
>>   end record;

> I'm not following this one at all.  Where is the cached data?

As I said, it goes in the record with the access value.  The caller has 
to declare a local copy to make a call, and that stores the data.  I 
much prefer the more Ada-like abstraction where the user instantiates a 
generic, and the generic has both local data and a (local) instance of 
the subroutine.

> Thank you so much, Robert, for taking the time to reply to my question.

It is an interesting question, and as a result I may actually advocate a 
language change.  If we had the equivalent of access discriminants for 
Ada subprograms, this would be much cleaner.  I think that the right 
level of implementation is an attribute.  So that 
Some_Subprogram'Called_From gets you the called from address.  It is not 
the best solution for this problem, but there may be a way to define it 
to be useful in doing stack tracebacks. (Which argues for the system 
programming annex.)

-- 
                                         Robert I. Eachus

Ryan gunned down the last of his third white wine and told himself it 
would all be over in a few minutes.  One thing he'd learned from 
Operation Beatrix: This field work wasn't for him. --from Red Rabbit by 
Tom Clancy.




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 11:54 ` Gautier
@ 2003-09-25 16:14   ` Robert I. Eachus
  2003-09-25 18:06   ` Tad Ashlock
  1 sibling, 0 replies; 28+ messages in thread
From: Robert I. Eachus @ 2003-09-25 16:14 UTC (permalink / raw)


Gautier wrote:

> BTW: what this solution with pointers is about ?!

A way of avoiding the generic instantiation approach you used. ;-)  It 
was more of a gedanken experiment to show that I could make a procedure 
that emulated self-modifying code.

-- 
                                       Robert I. Eachus

Ryan gunned down the last of his third white wine and told himself it 
would all be over in a few minutes.  One thing he'd learned from 
Operation Beatrix: This field work wasn't for him. --from Red Rabbit by 
Tom Clancy.




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 13:41 ` Stephen Leake
@ 2003-09-25 17:23   ` Tad Ashlock
  0 siblings, 0 replies; 28+ messages in thread
From: Tad Ashlock @ 2003-09-25 17:23 UTC (permalink / raw)


Stephen Leake <Stephe.Leake@nasa.gov> wrote in message news:<uhe310yvm.fsf@nasa.gov>...
> taashlo@sandia.gov writes:
[snip]
> Just declare each "hidden static" variable directly, and call 'bar'. 
> 
> Yes, it's a few more lines of code, but it's worth it!
> 
> > for (int i = 0; i < 55; ++i) {
> >    switch (wozit()) {
> >       case 1: BAR(10); break;
> >       case 2: BAR(20); break;
> >       case 3: BAR(30); break;
> >    }
> > }
> 
> This would become:
> 
> package foo
>     t_1 : s;
>     t_2 : s;
>     t_3 : s;
> 
> procedure Something is
> begin
>     for i in 1 .. 54 loop
>        case wozit is
>        when 1 =>
>            bar (t_1, 10);
>        when 2 =>
>            bar (t_2, 20);
>        when 3 =>
>            bar (t_3, 30);
>        end case;
>     end loop;
> end Something;
> 
> end foo;
> 
> You may complain this is using "ugly global variables", and it is. So is the
> C code. Using macros to hide doesn't really make it prettier :).

If the problem with this was just the ugliness, then I wouldn't have a
problem! :-)  The real problem with this is that a typo (e.g. t_2
instead of t_3) would be impossible for the compiler to catch, and
would produce a subtle bug where the subsequent behavior of a call
to bar() would depend on which call executed first.

The C++ implementation doesn't have *this* problem.

> A better way is to make 't' part of a state object that is passed in
> to Something, but that's left as an exercise for the reader.

This (I believe) would have the same problem I noted above.

Thank you,
Tad



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 11:54 ` Gautier
  2003-09-25 16:14   ` Robert I. Eachus
@ 2003-09-25 18:06   ` Tad Ashlock
  2003-09-28  8:42     ` Gautier Write-only
  1 sibling, 1 reply; 28+ messages in thread
From: Tad Ashlock @ 2003-09-25 18:06 UTC (permalink / raw)


gautier_niouzes@hotmail.com (Gautier) wrote in message news:<17cd177c.0309250354.77444ccd@posting.google.com>...
> Something like that:
> 
> with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO;
> 
> procedure Test_Static_in_generic is
> 
>   type S is array(0..7) of Integer;
> 
>   generic
>     foo: Integer;
>   package P is
>     procedure BAR;
>   end;
>   
>   package body P is
>     initialized: Boolean:= False;
>     cache: S;
>     procedure BAR is
>     begin
>       if not initialized then
>         cache:= (others=> foo);
>         initialized:= True;
>       end if;
>       Put(cache(cache'last));
>     end;
>   end;
> 
>   package P10 is new P(10);
>   package P20 is new P(20);
>   package P30 is new P(30);
> 
> begin
>   for count in 1..4 loop
>     for i in 1..3 loop
>       case i is
>         when 1=> P10.BAR;
>         when 2=> P20.BAR;
>         when 3=> P30.BAR;
>       end case;
>     end loop;
>     New_Line;
>   end loop;
> end;

What happens to cache when Test_Static_in_generic exits?  In the
C++ implementation, the next time Test_Static_in_generic is
entered, the individual cache's still retain their previous
contents.  Is that the case here?  (Sorry, but I'm not familar
enough with Ada to be certain about how a generic package defined
within a procedure behaves exactly.)

> ?
> 
> BTW: what this solution with pointers is about ?!

BAR() is actually used for performing static state transitions within
a hierarchical state machine.  See
<http://www.quantum-leaps.com/cookbook/recipes.htm>
and look at Q_TRAN() (which I renamed BAR() for my example).

The first time a particular transition is made, the path to the
target state must be discovered.  But subsequent transitions
can use the cached information.  This is how the current C++
implementation works.  As an extension, I'd like to eventually
find a way of "pre-caching" the transition information during
initialization.  But for a first attempt, I would like my Ada95
port to behave, as much as possible, like the original C++.

Thank you,
Tad



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 16:11     ` Robert I. Eachus
@ 2003-09-25 18:13       ` Randy Brukardt
  2003-09-25 23:40         ` Robert I. Eachus
  0 siblings, 1 reply; 28+ messages in thread
From: Randy Brukardt @ 2003-09-25 18:13 UTC (permalink / raw)


"Robert I. Eachus" <rieachus@attbi.com> wrote in message
news:3F7313BD.2050602@attbi.com...
> It is an interesting question, and as a result I may actually advocate a
> language change.  If we had the equivalent of access discriminants for
> Ada subprograms, this would be much cleaner.  I think that the right
> level of implementation is an attribute.  So that
> Some_Subprogram'Called_From gets you the called from address.  It is not
> the best solution for this problem, but there may be a way to define it
> to be useful in doing stack tracebacks. (Which argues for the system
> programming annex.)

That's the idea behind AI-282. As I recall, we decided we didn't like the
original proposal, but Bob and Tuck had a better idea. (Which we're still
waiting for.)

                    Randy.






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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 15:57       ` Robert I. Eachus
@ 2003-09-25 19:09         ` tmoran
  2003-09-29 14:57           ` taashlo
  0 siblings, 1 reply; 28+ messages in thread
From: tmoran @ 2003-09-25 19:09 UTC (permalink / raw)


I remain confused.  I thought the OP wanted something that used storage
that invisibly depended on the static *location* of a call, and that's
why he used separate "static" variables.
> > cache information for each unique call to bar() such that subsequent
> > calls from that location can be processed *much* faster.
          ^^^^^^^^^^^^^^^^^^
I gather that's not the correct interpretation.  If he is willing to
manually specify the particular cache by
  BAR(10);  -- cache number 10
or
  package P10 is new P(10);  -- cache number 10
  package P20 is new P(20);  -- cache number 20
  package P30 is new P(30);  -- cache number 30
or
  Local_Pointer.SP.all(3, Local_Pointer);   -- cache number 3

then why not just:

  type s is record ...
  t : array(1 .. 100) of s;  -- array of caches
  ...
  for i in 0 .. 54 loop
    case wozit is
      when 1 => bar(cache_to_use=>t( 3));
      when 2 => bar(cache_to_use=>t(20));
      when 3 => bar(cache_to_use=>t(14));
    end case;
  end loop;

To somewhat change the example, suppose we want to keep track of how
often different sections of code are run.  The usual method is to
do something like
  type sections is (initialization, processing_input, ...
and sprinkle around statements like
  log(initialization);
  ...
  log(process input);
Suppose that for some reason we don't just want to set a boolean, or
a counter for each section, but we want to raise an exception if any
section is run more than 100 times (maybe we're worried its RAM area
will die of overuse or something ;)  Then in C we could

void log(int *t){if(*t++ > 100)raise_the_exception;}
#define LOG if (1) { \
   static int t_; \
   log(&t_);\
 } else ((void)0)

and then we could sprinkle the statement
  LOG;
around in the program.  How could one implement a similar thing in Ada,
where the "log" call does not need a parameter, but implicitly has one
by virtue of its static location in the code?



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 18:13       ` Randy Brukardt
@ 2003-09-25 23:40         ` Robert I. Eachus
  0 siblings, 0 replies; 28+ messages in thread
From: Robert I. Eachus @ 2003-09-25 23:40 UTC (permalink / raw)


Randy Brukardt wrote:

> That's the idea behind AI-282. As I recall, we decided we didn't like the
> original proposal, but Bob and Tuck had a better idea. (Which we're still
> waiting for.)

I rechecked, and what we are talking about here is not provided in 
AI-282.  I probably want to think about it some more, but it would be a 
tremendous debugging aid without much cost.  This particular attribute 
would be the address from which the current instance of the enclosing 
procedure is called:  Some_Program'Call_Address.  Thinking about it, 
might as well make it the return address.  That is what is on the stack 
and is always very easy to get.  (I would advocate that the attribute 
should only be usable within the direct scope of the the named 
procedure.  (In other words, we don't want to favor implementations with 
static links.  Or maybe both static link and display implementations can 
do it fairly easily.)

The real use for this is where a subroutine has to do "extra" checking 
when it is called from user code instead of OS code.   It also makes 
writing some OS utilities marginally easier.

-- 
                                     Robert I. Eachus

Ryan gunned down the last of his third white wine and told himself it 
would all be over in a few minutes.  One thing he'd learned from 
Operation Beatrix: This field work wasn't for him. --from Red Rabbit by 
Tom Clancy.




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 18:06   ` Tad Ashlock
@ 2003-09-28  8:42     ` Gautier Write-only
  2003-09-28 20:06       ` Jeffrey Carter
  0 siblings, 1 reply; 28+ messages in thread
From: Gautier Write-only @ 2003-09-28  8:42 UTC (permalink / raw)


Tad Ashlock:

> What happens to cache when Test_Static_in_generic exits?  In the
> C++ implementation, the next time Test_Static_in_generic is
> entered, the individual cache's still retain their previous
> contents.  Is that the case here?  (Sorry, but I'm not familar
> enough with Ada to be certain about how a generic package defined
> within a procedure behaves exactly.)

Since the cache is defined inside of "Test_Static_in_generic" it
is effectively lost when at exit. If you define it outside of it
(e.g. in a package at the same level as Test_Static_in_generic)
it will remain. If Test_Static_in_generic is at "level 0" it could
look like this:

--8<----8<----8<----8<----8<----8<--
package BARs is

  type S is array(0..7) of Integer;

  generic
    foo: Integer;
  package P is
    procedure BAR;
  end;
  
end;

with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO;

package body BARs is

  package body P is
    initialized: Boolean:= False;
    cache: S;
    procedure BAR is
    begin
      if not initialized then
        cache:= (others=> foo);
        New_Line;
        Put_Line("[Filling cache for instance]" & Integer'image(foo));
        initialized:= True;
      end if;
      Put(cache(cache'last));
    end;
  end;

end BARs;

with BARs;

pragma Elaborate_all(BARs);

package Inst_BARs is

  package P10 is new BARs.P(10);
  package P20 is new BARs.P(20);
  package P30 is new BARs.P(30);

end;

with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO;
with Inst_Bars; use Inst_Bars;

procedure Test_Static_in_generic_2 is

begin
  Put_Line("Starting...");
  for count in 1..4 loop
    for i in 1..3 loop
      case i is
        when 1=> P10.BAR;
        when 2=> P20.BAR;
        when 3=> P30.BAR;
      end case;
    end loop;
    New_Line;
  end loop;
end;

with Test_Static_in_generic_2;

procedure Test_Static_in_generic_3 is
begin
  for i in 1..3 loop
    Test_Static_in_generic_2;
  end loop;
end;
--8<----8<----8<----8<----8<----8<--

But you also could avoid these instances (as translation of
expanded macros) by making an array of caches, according to
the efficiency tradeoff "place taken by the machine code of
instances (and RAM caching!)" vs. "access to 1 cache via the
array (once per call)". To be tested...

HTH
________________________________________________________
Gautier  --  http://www.mysunrise.ch/users/gdm/gsoft.htm

NB: For a direct answer, e-mail address on the Web site!



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-28  8:42     ` Gautier Write-only
@ 2003-09-28 20:06       ` Jeffrey Carter
  2003-09-29  2:13         ` Gautier Write-only
  0 siblings, 1 reply; 28+ messages in thread
From: Jeffrey Carter @ 2003-09-28 20:06 UTC (permalink / raw)


Gautier Write-only wrote:

> package BARs is
> 
>   type S is array(0..7) of Integer;
> 
>   generic
>     foo: Integer;
>   package P is
>     procedure BAR;
>   end;
>   
> end;
> 
> with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO;
> 
> package body BARs is
> 
>   package body P is
>     initialized: Boolean:= False;
>     cache: S;
>     procedure BAR is
>     begin
>       if not initialized then
>         cache:= (others=> foo);
>         New_Line;
>         Put_Line("[Filling cache for instance]" & Integer'image(foo));
>         initialized:= True;
>       end if;
>       Put(cache(cache'last));
>     end;
>   end;
> 
> end BARs;

There seems to be no need for type S. Why not have

generic
    Foo : Integer;
package BARs is
    procedure BAR;
end BARs;

with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
package body BARs is
    Cache : Integer := Foo;

    procedure BAR is
    begin
        Put (Cache);
    end BAR;
end BARs;

-- 
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




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-28 20:06       ` Jeffrey Carter
@ 2003-09-29  2:13         ` Gautier Write-only
  0 siblings, 0 replies; 28+ messages in thread
From: Gautier Write-only @ 2003-09-29  2:13 UTC (permalink / raw)


Jeffrey Carter:

# There seems to be no need for type S.

It comes from the OP's code skeleton:
   struct s {
      int data[8];
   };

I fill it with redundant information for the fun of it...
________________________________________________________
Gautier  --  http://www.mysunrise.ch/users/gdm/gsoft.htm

NB: For a direct answer, e-mail address on the Web site!



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-25 19:09         ` tmoran
@ 2003-09-29 14:57           ` taashlo
  2003-09-29 18:12             ` Jeffrey Carter
  2003-09-30  2:45             ` Robert I. Eachus
  0 siblings, 2 replies; 28+ messages in thread
From: taashlo @ 2003-09-29 14:57 UTC (permalink / raw)


tmoran@acm.org writes:
> I remain confused.  I thought the OP wanted something that used storage
> that invisibly depended on the static *location* of a call, and that's
> why he used separate "static" variables.

Your interpretation is precisely correct.

> > > cache information for each unique call to bar() such that subsequent
> > > calls from that location can be processed *much* faster.
>           ^^^^^^^^^^^^^^^^^^
> I gather that's not the correct interpretation.  If he is willing to
> manually specify the particular cache by
>   BAR(10);  -- cache number 10
> or
>   package P10 is new P(10);  -- cache number 10
>   package P20 is new P(20);  -- cache number 20
>   package P30 is new P(30);  -- cache number 30
> or
>   Local_Pointer.SP.all(3, Local_Pointer);   -- cache number 3
>
> then why not just:
>
>   type s is record ...
>   t : array(1 .. 100) of s;  -- array of caches
>   ...
>   for i in 0 .. 54 loop
>     case wozit is
>       when 1 => bar(cache_to_use=>t( 3));
>       when 2 => bar(cache_to_use=>t(20));
>       when 3 => bar(cache_to_use=>t(14));
>     end case;
>   end loop;
>
> To somewhat change the example, suppose we want to keep track of how
> often different sections of code are run.  The usual method is to
> do something like
>   type sections is (initialization, processing_input, ...
> and sprinkle around statements like
>   log(initialization);
>   ...
>   log(process input);
> Suppose that for some reason we don't just want to set a boolean, or
> a counter for each section, but we want to raise an exception if any
> section is run more than 100 times (maybe we're worried its RAM area
> will die of overuse or something ;)  Then in C we could
>
> void log(int *t){if(*t++ > 100)raise_the_exception;}
> #define LOG if (1) { \
>    static int t_; \
>    log(&t_);\
>  } else ((void)0)
>
> and then we could sprinkle the statement
>   LOG;
> around in the program.  How could one implement a similar thing in Ada,
> where the "log" call does not need a parameter, but implicitly has one
> by virtue of its static location in the code?

Thank you so much for this tremendously clearer wording and example of
my question.  You have precisely caught the essence of what I'm trying
to accomplish.  Hopefully your wording and my discussion (below) of the
weaknesses of the solutions provided so far will help others see a
solution to the problem that's as good as, or better, than the C++
solution.

(If the following discussion sounds ungrateful, I apologize.  It isn't
intended to be so.  On the contrary, I'm quite thankful for, and
somewhat impressed by, the amount of attention my little problem has
received.)

Hyman Rosen had the following to say:

"If you want to have a memoizing function, the job of caching old values
belongs to the function itself, not scattered in macros and variables
all over the place."

Which, in general, I agree with.  But in this specific case, what do you
use to index the caches?  Remember that there has to be a separate cache
for each unique call site.  (E.g. if there are three calls made to this
function *in your source code*, then there will need to be three
separate caches.)  There's only two options that I can think of:

1) Use a unique ID for each call.  But this turns into a maintenance
nightmare when you get a few hundred calls or more.

2) Use the return address of the call.  But there was a short discussion
about how Ada doesn't currently have a way of accessing the return
address of a subprogram.

Robert I. Eachus, Stephen Leake, and Gautier all replied with solutions
that required creating a unique identifier for each call.  But this,
like solution #1 above, doesn't scale well.  The C++ solution, which
uses an anonymous static variable structure automatically associated
with each call, does scale well.  You could call the function 1000
times without having to worry about creating a unique ID or allocating a
unique cache structure.

Although I originally thought that the C++ implementation was a bit of a
hack, I'm beginning to think that this is a somewhat elegant solution to
a problem that can't be elegantly solved in Ada.  By elegant, in this
case, I mean simple, extensible, and maintainable.

I would sure like to be proved wrong, though.

Thanks again,
Tad



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-29 14:57           ` taashlo
@ 2003-09-29 18:12             ` Jeffrey Carter
  2003-09-29 18:51               ` taashlo
  2003-09-30  2:45             ` Robert I. Eachus
  1 sibling, 1 reply; 28+ messages in thread
From: Jeffrey Carter @ 2003-09-29 18:12 UTC (permalink / raw)


taashlo@sandia.gov wrote:

> tmoran@acm.org writes:
 >
>> void log(int *t){if(*t++ > 100)raise_the_exception;}
>> #define LOG if (1) { \
>>    static int t_; \
>>    log(&t_);\
>>  } else ((void)0)

Are ints in C++ automatically initialized to zero? I admit my C++ is not 
very good, but this looks as if it could raise the exception after fewer 
than 100 invokations, or after 101 invocations. (I assume this is C++ 
and not C because I don't recall C having exceptions.)

> There's only two options that I can think of:
> 
> 1) Use a unique ID for each call.  But this turns into a maintenance
> nightmare when you get a few hundred calls or more.
> 
> 2) Use the return address of the call.  But there was a short discussion
> about how Ada doesn't currently have a way of accessing the return
> address of a subprogram.

3. Use a unique instantiation of a generic package for each call. The 
instantiations of the package must be at a nesting level that ensures 
that they will exist for the entire lifetime of the executable region in 
which they are used.

-- 
Jeff Carter
"You tiny-brained wipers of other people's bottoms!"
Monty Python & the Holy Grail
18




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-29 18:12             ` Jeffrey Carter
@ 2003-09-29 18:51               ` taashlo
  2003-09-30  1:32                 ` Jeffrey Carter
  0 siblings, 1 reply; 28+ messages in thread
From: taashlo @ 2003-09-29 18:51 UTC (permalink / raw)


Jeffrey Carter <spam@spam.com> writes:
> taashlo@sandia.gov wrote:
> > tmoran@acm.org writes:
>  >
> >> void log(int *t){if(*t++ > 100)raise_the_exception;}
> >> #define LOG if (1) { \
> >>    static int t_; \
> >>    log(&t_);\
> >>  } else ((void)0)
>
> Are ints in C++ automatically initialized to zero? I admit my C++ is
> not very good, but this looks as if it could raise the exception after
> fewer than 100 invokations, or after 101 invocations. (I assume this
> is C++ and not C because I don't recall C having exceptions.)

Yes, static ints will be initialized to zero.

> > There's only two options that I can think of:
> > 1) Use a unique ID for each call.  But this turns into a maintenance
> > nightmare when you get a few hundred calls or more.
> > 2) Use the return address of the call.  But there was a short
> > discussion
> > about how Ada doesn't currently have a way of accessing the return
> > address of a subprogram.
>
> 3. Use a unique instantiation of a generic package for each call. The
> instantiations of the package must be at a nesting level that ensures
> that they will exist for the entire lifetime of the executable region
> in which they are used.

Won't a unique instantiation require a unique name?  This is the same
problem with item 1) above.  A single unique instantiation is no
problem, but try a thousand instantiations whose associated function
calls are scattered across fifty files.  Where using the wrong
instantiation can lead to subtle and difficult-to-reproduce errors.
This is not a solution that I'd voluntarily aim for.

> --
> Jeff Carter
> "You tiny-brained wipers of other people's bottoms!"
> Monty Python & the Holy Grail
> 18



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-29 18:51               ` taashlo
@ 2003-09-30  1:32                 ` Jeffrey Carter
  2003-09-30  1:54                   ` tmoran
  2003-09-30 13:02                   ` taashlo
  0 siblings, 2 replies; 28+ messages in thread
From: Jeffrey Carter @ 2003-09-30  1:32 UTC (permalink / raw)


taashlo@sandia.gov wrote:

> Jeffrey Carter <spam@spam.com> writes:
> 
>>>1) Use a unique ID for each call.  But this turns into a maintenance
>>>nightmare when you get a few hundred calls or more.
>>>2) Use the return address of the call.  But there was a short
>>>discussion
>>>about how Ada doesn't currently have a way of accessing the return
>>>address of a subprogram.
>>
>>3. Use a unique instantiation of a generic package for each call. The
>>instantiations of the package must be at a nesting level that ensures
>>that they will exist for the entire lifetime of the executable region
>>in which they are used.
> 
> Won't a unique instantiation require a unique name?  This is the same
> problem with item 1) above.  A single unique instantiation is no
> problem, but try a thousand instantiations whose associated function
> calls are scattered across fifty files.  Where using the wrong
> instantiation can lead to subtle and difficult-to-reproduce errors.
> This is not a solution that I'd voluntarily aim for.

We've gone from hundreds to thousands now? I can play that game, too. If 
you use millions of identical calls in your code, no one can maintain 
it, no matter what language or technique you use.

There are significant differences between using a unique ID and a unique 
instantiation name. With IDs you have to add new IDs and modify the code 
that interprets the ID as well as insert the new call for a new 
location. With an instantiation you add the new instantiation and the 
new call. You need never touch the existing code except to delete a 
call, and that, of course, is true regardless of the technique used. 
With a good process for choosing the next instantiation name, you'll 
never create a problem.

All this is sort of irrelevant, though. We don't know what you're trying 
to accomplish, just a technique you're trying to use. Without that 
knowledge, we can't really tell how to advise you.

-- 
Jeff Carter
"You've got the brain of a four-year-old boy,
and I bet he was glad to get rid of it."
Horse Feathers
47




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-30  1:32                 ` Jeffrey Carter
@ 2003-09-30  1:54                   ` tmoran
  2003-09-30 13:02                   ` taashlo
  1 sibling, 0 replies; 28+ messages in thread
From: tmoran @ 2003-09-30  1:54 UTC (permalink / raw)


>instantiation name. With IDs you have to add new IDs and modify the code
>that interprets the ID as well as insert the new call for a new
>location. With an instantiation you add the new instantiation and the
>new call. You need never touch the existing code except to delete a
  With instantiations, he must add at a global level
    procedure log872 is new log;
and then in his code insert a call to "log872;"
If he's doing that, why not simply
  cache: array(1 .. 872) of cached_data_type;
and
  cache(872) := stuff;

>All this is sort of irrelevant, though. We don't know what you're trying
>to accomplish, just a technique you're trying to use. Without that

  A not necessarily portable, but probably works, method of course is to
raise a dummy exception occurrence and parse the exception information for
a file/line number, ie, a static location in the source code.

  Certainly it's very questionable to use a static code location as a key
for a cache database.  I'd agree that a function ought to maintain its own
cache invisibly to the outside world, just like any other part of its
internal algorithm.  But my example of the log that keeps track of code
usage is an (admittedly stretched) example of a use for the static
location information.



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-29 14:57           ` taashlo
  2003-09-29 18:12             ` Jeffrey Carter
@ 2003-09-30  2:45             ` Robert I. Eachus
  2003-09-30  3:24               ` tmoran
  1 sibling, 1 reply; 28+ messages in thread
From: Robert I. Eachus @ 2003-09-30  2:45 UTC (permalink / raw)


taashlo@sandia.gov wrote:

> Although I originally thought that the C++ implementation was a bit of a
> hack, I'm beginning to think that this is a somewhat elegant solution to
> a problem that can't be elegantly solved in Ada.  By elegant, in this
> case, I mean simple, extensible, and maintainable.

Remember that the code I posted was more of an existance proof rather 
than an elegant implementation of what you were asking for.  I could 
probably come up with a version that hid away most of the details from a 
user, but I wanted it to be possible to see what was going on.

Having said that, the general Ada solution to this type of problem is 
generics.  Generics allow you to have two points at which variables are 
bound to parameters, which allows for a much greater richness of 
application than the C++ code.  The flip side of that is that the C++ 
code is simpler.  In Ada you can have one memo per call site, in C++ you 
must.  Which is better?  What do you want to do?  The Ada approach is 
more general, but the cost is writing a bit more per call.

As for the (very gory as submitted) version I posted, I think that a 
return address attribute if available would be much better.  You are 
free to add it to GNAT, or more to the point write a Return_Address 
function and just use that.  Of course, such a function would not be 
portable, which is why I think that an attribute, possibly in Annex C 
would be a the best approach.  In fact, if someone wants to propose an 
Ada.Call_Stack package for Ada 0Y, I'd vote for it.  However, I am much 
more likely to try writing the package (for at least one GNAT version) 
first, and I suspect that the ARG as a whole would not vote for it this 
time around. (It is very late in the game.)

-- 
                                                     Robert I. Eachus

"Quality is the Buddha. Quality is scientific reality. Quality is the 
goal of Art. It remains to work these concepts into a practical, 
down-to-earth context, and for this there is nothing more practical or 
down-to-earth than what I have been talking about all along...the repair 
of an old motorcycle."  -- from Zen and the Art of Motorcycle 
Maintenance by Robert Pirsig




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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-30  2:45             ` Robert I. Eachus
@ 2003-09-30  3:24               ` tmoran
  0 siblings, 0 replies; 28+ messages in thread
From: tmoran @ 2003-09-30  3:24 UTC (permalink / raw)


>Remember that the code I posted was more of an existance proof rather
>...
>but I wanted it to be possible to see what was going on.
  I'm afraid I still didn't understand what was going on.  Could you
post an example that, say, raises a Constraint_Error the 3rd time
that a call "strike;" is made from a particular location?



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-30  1:32                 ` Jeffrey Carter
  2003-09-30  1:54                   ` tmoran
@ 2003-09-30 13:02                   ` taashlo
  2003-09-30 20:25                     ` Jeffrey Carter
  1 sibling, 1 reply; 28+ messages in thread
From: taashlo @ 2003-09-30 13:02 UTC (permalink / raw)


Jeffrey Carter <spam@spam.com> writes:
> taashlo@sandia.gov wrote:
> > Jeffrey Carter <spam@spam.com> writes:
> >
> >>>1) Use a unique ID for each call.  But this turns into a maintenance
> >>>nightmare when you get a few hundred calls or more.
> >>>2) Use the return address of the call.  But there was a short
> >>>discussion
> >>>about how Ada doesn't currently have a way of accessing the return
> >>>address of a subprogram.
> >>
> >>3. Use a unique instantiation of a generic package for each call. The
> >>instantiations of the package must be at a nesting level that ensures
> >>that they will exist for the entire lifetime of the executable region
> >>in which they are used.
> > Won't a unique instantiation require a unique name?  This is the same
> > problem with item 1) above.  A single unique instantiation is no
> > problem, but try a thousand instantiations whose associated function
> > calls are scattered across fifty files.  Where using the wrong
> > instantiation can lead to subtle and difficult-to-reproduce errors.
> > This is not a solution that I'd voluntarily aim for.
> 
> We've gone from hundreds to thousands now? I can play that game,
> too. If you use millions of identical calls in your code, no one can
> maintain it, no matter what language or technique you use.

I went up one order of magnitude, you went up four.  You appear to play
the game much better than I. :-)

> There are significant differences between using a unique ID and a
> unique instantiation name. With IDs you have to add new IDs and modify
> the code that interprets the ID as well as insert the new call for a
> new location.

If the ID is simply an index into an array of caches, it doesn't require
you to "modify the code that interprets the ID".  So it's down to "add
new IDs" and "insert the new call".

> With an instantiation you add the new instantiation and
> the new call.

Same as above.  Please give more detail on the "significant
differences".

> You need never touch the existing code except to delete
> a call, and that, of course, is true regardless of the technique
> used.

But don't forget to delete the associated generic instantiation, too.

> With a good process for choosing the next instantiation name,
> you'll never create a problem.

I don't know of a manual process that can prevent me from being human
and therefore making mistakes.  And the penalty for making a mistake of
accidentally re-using an instantiation name is that the location that
gets called first will determine the contents of the cache for both
locations.  In a dynamic system where the one that gets called first
changes from execution to execution, this could result in a *very*
difficult bug to find.

> All this is sort of irrelevant, though. We don't know what you're
> trying to accomplish, just a technique you're trying to use. Without
> that knowledge, we can't really tell how to advise you.

In another posting, I did explain what I was trying to accomplish.  (I
didn't post it originally because I was afraid that the discussion would
get sidetracked for various reasons.)

I'm attempting to port a C++ library that supports implementing
hierarchical state machines (think UML statecharts) directly in a
programming language (as opposed to using a tool to generate the code).
The function in question performs a state transition.  You pass in the
target state and it discovers the path from the current state to the
target state.  It then caches this information so that subsequent calls
from the same location don't need to perform the same "discovery"
process.

Yes, the transition path could (possibly) be cached by the state
transition function based on the starting state and the target state.
(In Ada, is it possible to somehow use the *value* of subprogram access
parameters?  In this system, a state is defined as a subprogram access
type.)  You can use dynamic allocation.  Or you can reserve enough space
so that you can cache all state transitions.  Or you can can have some
combination of both so that when the reserved space is filled, more will
be allocated.  Or you can come up with more options that I haven't
thought of yet.  And if I was just developing an application, a large
number of choices is great.  But for porting a library whose utility
ranges from GUI applications running on workstations to small embedded
systems, having to make a choice will certainly narrow this range.  And
it won't win me any converts from C++ because the C++ implementation
didn't require you to know ahead of time how many state transitions
there are, or suffer the overhead of dynamic allocation, or have to come
up with a unique name for each cache.

One solution would be to come up with a way to automatically pre-cache
the state transitions during initialization.  This way, you could
statically reserve cache space and if you didn't have enough cache space
reserved, you would find out during start-up.  I'll look into this.

> --
> Jeff Carter
> "You've got the brain of a four-year-old boy,
> and I bet he was glad to get rid of it."
> Horse Feathers
> 47

BTW, if you have 50 state machines (which is reasonable for a
medium-large system) times 20 state transitions per state machine (some
will have more, some less, but this sounds reasonable to me) gives 1000
calls to the state transition function.

Tad



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

* Re: How to Emulate C++ Macro with Static Local Variable?
  2003-09-30 13:02                   ` taashlo
@ 2003-09-30 20:25                     ` Jeffrey Carter
  0 siblings, 0 replies; 28+ messages in thread
From: Jeffrey Carter @ 2003-09-30 20:25 UTC (permalink / raw)


taashlo@sandia.gov wrote:

> I went up one order of magnitude, you went up four.  You appear to play
> the game much better than I. :-)

There's no profit in thinking small :)

> If the ID is simply an index into an array of caches, it doesn't require
> you to "modify the code that interprets the ID".  So it's down to "add
> new IDs" and "insert the new call".
> 
>>With an instantiation you add the new instantiation and
>>the new call.
> 
> Same as above.  Please give more detail on the "significant
> differences".

And if the ID is NOT simply an index into an array? Generics handle that
case exactly the same way. IDs do not; in the general case, you have 
some processing to deal with the IDs.

> But don't forget to delete the associated generic instantiation, too.

You probably want to delete the instantiation, but you don't have to. A 
good compiler will eliminate the resulting code; in the worst case, you 
have unused code.

> I don't know of a manual process that can prevent me from being human
> and therefore making mistakes.  And the penalty for making a mistake of
> accidentally re-using an instantiation name is that the location that
> gets called first will determine the contents of the cache for both
> locations.  In a dynamic system where the one that gets called first
> changes from execution to execution, this could result in a *very*
> difficult bug to find.

Yes, but this is the argument against using C/++ and for using Ada. It's 
very easy to make simple mistakes that compile and run and are very 
difficult to find. If that's acceptable to you in C/++ it should be 
acceptable in Ada.

> I'm attempting to port a C++ library that supports implementing
> hierarchical state machines (think UML statecharts) directly in a
> programming language (as opposed to using a tool to generate the code).
> The function in question performs a state transition.  You pass in the
> target state and it discovers the path from the current state to the
> target state.  It then caches this information so that subsequent calls
> from the same location don't need to perform the same "discovery"
> process.
> 
> Yes, the transition path could (possibly) be cached by the state
> transition function based on the starting state and the target state.
> (In Ada, is it possible to somehow use the *value* of subprogram access
> parameters?  In this system, a state is defined as a subprogram access
> type.)  You can use dynamic allocation.  Or you can reserve enough space
> so that you can cache all state transitions.  Or you can can have some
> combination of both so that when the reserved space is filled, more will
> be allocated.  Or you can come up with more options that I haven't
> thought of yet.  And if I was just developing an application, a large
> number of choices is great.  But for porting a library whose utility
> ranges from GUI applications running on workstations to small embedded
> systems, having to make a choice will certainly narrow this range.  And
> it won't win me any converts from C++ because the C++ implementation
> didn't require you to know ahead of time how many state transitions
> there are, or suffer the overhead of dynamic allocation, or have to come
> up with a unique name for each cache.

This is much clearer. Yes, access-to-subprogram values are no different 
than any other access values. You can store them and compare them. So a 
solution based on the 2 states sounds like a good approach to use.

> One solution would be to come up with a way to automatically pre-cache
> the state transitions during initialization.  This way, you could
> statically reserve cache space and if you didn't have enough cache space
> reserved, you would find out during start-up.  I'll look into this.

Could you do a "2-pass" process? The 1st pass would tell you how big 
your cache needs to be; then you could statically allocate enough space 
(Ada allows you to statically allocate space based on values not known 
until run time) and fill it during the 2nd pass.

As a compromise, starting with a default amount of space that's usually 
big enough, and doubling it if you run out of space, is usually 
acceptable, even for some embedded systems.

-- 
Jeff Carter
"All citizens will be required to change their underwear
every half hour. Underwear will be worn on the outside,
so we can check."
Bananas
29




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

end of thread, other threads:[~2003-09-30 20:25 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-09-24 22:06 How to Emulate C++ Macro with Static Local Variable? taashlo
2003-09-24 23:44 ` Robert I. Eachus
2003-09-25  3:27   ` Hyman Rosen
2003-09-25  4:53     ` tmoran
2003-09-25 15:57       ` Robert I. Eachus
2003-09-25 19:09         ` tmoran
2003-09-29 14:57           ` taashlo
2003-09-29 18:12             ` Jeffrey Carter
2003-09-29 18:51               ` taashlo
2003-09-30  1:32                 ` Jeffrey Carter
2003-09-30  1:54                   ` tmoran
2003-09-30 13:02                   ` taashlo
2003-09-30 20:25                     ` Jeffrey Carter
2003-09-30  2:45             ` Robert I. Eachus
2003-09-30  3:24               ` tmoran
2003-09-25 13:43     ` Stephen Leake
2003-09-25 12:59   ` Tad Ashlock
2003-09-25 16:11     ` Robert I. Eachus
2003-09-25 18:13       ` Randy Brukardt
2003-09-25 23:40         ` Robert I. Eachus
2003-09-25 11:54 ` Gautier
2003-09-25 16:14   ` Robert I. Eachus
2003-09-25 18:06   ` Tad Ashlock
2003-09-28  8:42     ` Gautier Write-only
2003-09-28 20:06       ` Jeffrey Carter
2003-09-29  2:13         ` Gautier Write-only
2003-09-25 13:41 ` Stephen Leake
2003-09-25 17:23   ` Tad Ashlock

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