Simple object persistence in a binary

Hi,
Is there a way to achieve persistence without manually managing files ?
I want a lookup table that isn’t redone at every run. Shared_Passive would do that but it requires the object to be library-level, ok, and pure, which is not ok. I want to fill the sin table with Elementary_Functions, not by hand !
The whole thing should be done at compile time and written in the DATA section. I looked up pragma Persistent_BSS but it requires an absence of explicit initialization so I have no clue how it is used, nor do I find exemples.

Also, does the inverse of 'Truncation exist ? Meaning, you get always the integral most further from zero, no matter if X is negative or positive. So I could linearly interpolate values between two integral angle values. If it does not exist, I’ll use 'Ceiling or 'Floor depending on the sign.

This has nothing to do with persistence. In similar cases I write a small Ada program that generates an Ada package, e.g. for handling Unicode classifications or embedding images in GTK.

Look into the new Static aspect. There’s absolutely no reason why gnat shouldn’t generate code at compile time and dump the data in the data section, not the bss, the bss is uninitialised.


Also, does the inverse of 'Truncation exist ? Meaning, you get always the integral most further from zero, no matter if X is negative or positive. So I could linearly interpolate values between two integral angle values. If it does not exist, I’ll use 'Ceiling or 'Floor depending on the sign.

No, there is no “Round-away-from-zero” attribute function. Using a combination of 'Ceiling and 'Floor as you suggest seems like the best solution.

Static doesn’t work.

main.adb:11:61: error: static expression function requires potentially static expression
main.adb:11:125: error: aspect “static” requires function with result of a static subtype

I don’t think it can be used for any interesting calculation yet.

with Ada.Text_IO, Ada.Numerics.Elementary_Functions;
use Ada.Text_IO;
procedure main is
	package lookuptable is
		use Ada.Numerics.Elementary_Functions;
		use Ada.Numerics;
		function Sin (D: Float) return Float;
		function Cos (D: Float) return Float;
		-- function Tan (D: Float) return Float;
		type LookupTable is array (Positive range 1..90) of Float;
		function FillTableSin return LookupTable is (for I in Lookuptable'Range => Sin(Float(I) * Pi / 180.0)) with Static;
		function FillTableCos return LookupTable is (for I in Lookuptable'Range => Cos(Float(I) * Pi / 180.0)) with Static;
		function FillTableTan return LookupTable is (for I in 1..89 => Tan(Float(I) * Pi / 180.0), 90 => <>) with Static;
		Sintable: constant Lookuptable := FillTableSin;
		Costable: constant Lookuptable := FillTableCos;
		Tantable: constant Lookuptable := FillTableTan;
	end lookuptable;
	package body lookuptable is
		subtype IntDegree is Integer range -359..359;
		function "mod" (D: Float; M: Positive) return Float is
			Value: Float := D;
		begin
			while Value > Float (M) loop
				Value := @ - Float (M); 
			end loop;
			return Value;
		end "mod";
		function Sin (D: Float) return Float is
			function Sin (D: IntDegree) return Float is
				(case D is when 0..90 => Sintable (D),
				when 91..180 => Sintable (180 - D),
				when 181..270 => - Sintable (D - 180),
				when 271..359 => - Sintable (360 - D),
				when others => - Sin (360 + D));
			Trunc: constant Float := Sin (Integer (Float'Truncation(D)) mod 360);
			Next : constant Float := Sin ((Integer (Float'Truncation(D)) + (if D > 0.0 then 1 else -1)) mod 360);
		begin
			return Trunc + (D mod 360) * (Next - Trunc);
		end Sin;
		function Cos (D: Float) return Float is
			function Cos (D: IntDegree) return Float is
				(case D is when 0..90 => Costable (D),
				when 91..180 => - Costable (180 - D),
				when 181..270 => - Costable (D - 180),
				when 271..359 => Costable (360 - D),
				when others => Cos (360 + D));
			Trunc: constant Float := Cos (Integer (Float'Truncation(D)) mod 360);
			Next : constant Float := Cos ((Integer (Float'Truncation(D)) + (if D > 0.0 then 1 else - 1)) mod 360);
		begin
			return Trunc + (D mod 360) * (Next - Trunc);
		end Cos;
	end lookuptable;
	use lookuptable;
begin
	Put_line (Sin(-23.23)'Image);
	Put_line (Cos(59.04)'Image);
end main;

Hi,

I believe in your case it cannot be done, but in general, it can. Here is how it would be for your case:

pragma Ada_2022;

with Ada.Text_IO, Ada.Numerics.Elementary_Functions;
use Ada.Text_IO;
procedure static is
	package lookuptable is
		use Ada.Numerics.Elementary_Functions;
      use Ada.Numerics;
      subtype IntDegree is Integer range -359..359;
		function Sin (D: IntDegree) return Float is
          (case D is when 0..90    =>   Sintable (D),
                     when 91..180  =>   Sintable (180 - D),
                     when 181..270 => - Sintable (D - 180),
				     when 271..359 => - Sintable (360 - D),
                     when others   => - Sin (360 + D)
          ) with Static;
             
        function Sin (D: Float) return Float is
          (
            ( 
            declare
               Trunc : constant Float := Sin (Integer (Float'Truncation(D)) mod 360);
               Next  : constant Float := Sin ((Integer (Float'Truncation(D)) + (if D > 0.0 then 1 else -1)) mod 360);
            begin
               Trunc + Float(Integer(D) mod 360) * (Next - Trunc)
            ) 
          ) with Static;
-- ...

Here is what is happening in your case and why even my solution is still not valid.

  1. The Static aspect can only be applied to expression functions (as you did correctly in your code).
  2. Static functions can only call Static code. This means, that your Sin and Cos functions, used in function FillTableSin return LookupTable is (for I in Lookuptable'Range => Sin(Float(I) * Pi / 180.0)) with Static;also need to become Static.
  3. Because of the above points, you need to turn Sin and Cos to expression functions. Which is what you can see in my code. I have splitted the IntDegree function as a separate one as it cannot be added to the declare block. I have also added them to the specification part of the package. This will work in pretty much any context… However…
  4. You have a dependency loop! Your Sin function requires the SinTable data. This data comes from the FillTableSin function, which requires Sin to be defined and computed. But this cannot happen as SinTable has not been filled!

Hopefully this answers your issue :slight_smile: I would recommend using the Sin function present in the Ada STL. However, I just checked (Elementary Functions), and the Elementary functions are not Static… This may be a good thing to propose to the ARG @sttaft

As expected. I’ll do that. Any subprogram not relying on an internal state or exterior I/O should be static iff parameters are. While it could be too much for the compiler to do that kind of analysis, it should clearly not be a problem for parameterless calls.
I guess I’ll output the aggregate needed (90 items is not too much…), as proposed above.

That might actually be a good use for separate… I’m not sure if a separate-subprogram can be implemented with an expression-function and static, but it should be able to.

(I would think that it would be ideal to have the body of the expression-function for something complex able to be “stashed away” in its own file, hence a valid [and new] case for separate.)

I talked with Randy, who basically told me “Just no”. “The concept of static as it is exists in Ada is a hack, it only applies to scalar and string types. User-defined types are never static.”. This has to do with code quality, which the manual does not address. A change would have to come from static analysis tools changing the object code. Not language.

Mmmm… This seems like a rather big limitation considering Ada’s design of user defined types and heavy handed compiler analysis checks… Maybe the Ada standard could be extended to explicitly extend Static for this case and maybe polish the concept a bit so that it is not a “hack”… Though I suppose GNAT is already doing much of the heavy lifting beyond the minimums indicated in the ARM…

Best,
Fer