1

I am planning to convert some programs written in C/C++ to Ada. These make heavy use of constant char literals often as ragged arrays like:

const char * stringsA = { "Up", "Down", "Shutdown" };

or string references in records like:

typedef struct
   {
   int something;
   const char * regexp;
   const char * errormsg
   } ERRORDESCR;
ERRORDESCR edscrs [ ] =
   {
    { 1, "regexpression1", "Invalid char in person name" },
    { 2, "regexp2", "bad bad" }
   };

The presets are calculated by the C/C++ compiler and I want the Ada compiler to be able to do that too.

I used Google and searched for ragged arrays but could only find two ways of presetting the strings. One in Rationale for Ada 95 by John Barnes and another at http://computer-programming-forum.com/44-ada/d4767ad6125feac7.htm. These are shown as stringsA and stringsB below. StringsA is defined in two stages, which is a bit tedious if there are hundreds of strings to set up. StringsB uses one step only, but is compiler dependent.

Question 1: are there other ways? Question 2: would the second stringsB work with GNAT Ada?

I have not started converting. The packages below are just for experimenting and teaching myself...

package ragged is
   type String_ptr is access constant String;
   procedure mydummy;
end ragged;

package body ragged is
   s1: aliased constant String := "Up";
   s2: aliased constant String := "Down";
   s3: aliased constant String := "Shutdown";

   stringsA: array (1 .. 3) of String_ptr :=
     (s1'Access, s2'Access, s3'Access); -- works

   stringsB: array (1 .. 3) of String_ptr :=
      (new String'("Up"), new String'("Down"),
      new String'("Shutdown"));  -- may work, compiler-dependent

   --   this would be convenient and clear...
   --stringsC: array (1 .. 3) of String_ptr :=
   -- ("Up", "Down", "Shutdown");  -- BUT Error, expected String_ptr values

   --stringsD: array (1 .. 3) of String_ptr :=
   --("Up"'Access, "Down"'Access, "Shutdown"'Access);  --Error - bad Access use

   --stringsE: array (1 .. 3) of String_ptr :=
   --(String_ptr("Up"), String_ptr("Down"),
   -- String_ptr("Shutdown"));  -- Error, invalid conversion

   procedure mydummy is
   begin
   null;
   end;
end ragged;
resander
  • 1,181
  • 2
  • 11
  • 15
  • 2
    Looks like a duplicate of http://stackoverflow.com/questions/41151494/ada-constant-array-of-string-literals – Jacob Sparre Andersen Jan 24 '17 at 21:12
  • I googled, but missed the similar posts. I made a test program and found that stringsA and stringsB work for GNAT Ada. However, both approaches are verbose and cluttered compared to the C version const char * strings[] = { "Up", "Down", "Shutdown" }; Ada newcomers with C, Java, JS and C# etc modern language background are likely to be disappointed. – resander Jan 25 '17 at 12:14
  • Why should your B example be compiler dependent? It is ugly, yes, but I can't see the compiler dependence. – Jacob Sparre Andersen Jan 25 '17 at 18:59
  • 1
    Jacob: I googled on 'Ragged array' and got matches. One match, http://coding.derkeiler.com/Archive/Ada/comp.lang.ada/2004-09/0746.html contains a reference to Tucker Taft who says the approach in example B is implementation-dependent. I don't have knowledge yet to make this kind of statement. – resander Jan 25 '17 at 20:14
  • 1
    I think Tucker’s point was that the optimisation (of not doing any actual allocations, and not emitting any runtime-code) is compiler-dependent. GNAT GPL 2016 does the optimisation. – Simon Wright Jan 26 '17 at 17:03
  • Wrt to C newcomers, Ada asks developer a question, and developer must answer it somehow. The "default" answers in C or JS are not default for Ada. How is memory going to be reclaimed. In C, it will be reclaimed with DLL unload, and it can cause dangling link. In JS, there is a reference mechanism. In Ada you can choose either, but it must be designed explicitly. – OCTAGRAM Feb 04 '17 at 13:31

2 Answers2

1

A little judicious operator overloading can do this in a less cluttered manner:

(Within the package body)

   function New_String(S : String) return String_Ptr is
   begin
      return new String'(S);
   end New_String;

   function "+" (S : String) return String_Ptr renames New_String;

Now you can do:

   stringsC: array (1 .. 3) of String_ptr := (+"Up", +"Down", +"Shutdown");
Marc C
  • 8,664
  • 1
  • 24
  • 29
  • That's really nice! Essentially same as the C-like languages. Will try and report back. – resander Jan 25 '17 at 19:49
  • I wrote a small test program for stringsC. The values came out correct, but were generated at run-time not at compile-time. I was setting break points at return new String'(S) and at the stringsC array declaration and both were triggered. I also used Debug/Data/Display at start of the main program to view stringsC(1)... and all came out NULL. When I displayed stringsA and stringsB at the start they came out correctly. I have not used Ada before, so I may be wrong. The testprogram is in the next answer box. – resander Jan 26 '17 at 15:44
  • GNAT appears to have circuitry to recognise that `new String’("Up")` can be optimised as we would like, but not `+"Up"`. Interestingly, using `-gnatG` shows that `StringsB` is represented internally just like `StringsA`! – Simon Wright Jan 26 '17 at 17:20
  • function "+" could have been defined from the beginning without rename – OCTAGRAM Feb 04 '17 at 13:28
0

Not enough space in comment for this Test program

package raggedtest is
   type String_ptr is access constant String;
   procedure mytest;
end raggedtest;

with ada.text_IO; use Ada.Text_IO;
package body raggedtest is
   s1: aliased constant String := "Up";
   s2: aliased constant String := "Down";
   s3: aliased constant String := "Shutdown";
   stringsA: array (1 .. 3) of String_ptr :=
     (s1'Access, s2'Access, s3'Access);

   stringsB: array (1 .. 3) of String_ptr :=
      (new String'("UpB"), new String'("DownB"),
       new String'("ShutdownB"));

   function New_String(S : String) return String_Ptr is
   begin
      return new String'(S);
   end New_String;
   function "+" (S : String) return String_Ptr renames New_String;
   stringsC: array (1 .. 3) of String_ptr := (+"UpC", +"DownC", +"ShutdownC");


   procedure mytest is
   begin
      put ( "s1A: " ); put( stringsA(1).all ); New_line;
      put ( "s2A " ); put( stringsA(2).all ); New_line;
      put ( "s3A: " ); put( stringsA(3).all ); New_line;

      put ( "s1B: " ); put( stringsB(1).all ); New_line;
      put ( "s2B " ); put( stringsB(2).all ); New_line;
      put ( "s3B: " ); put( stringsB(3).all ); New_line;

      put ( "s1C: " ); put( stringsC(1).all ); New_line;
      put ( "s2C " ); put( stringsC(2).all ); New_line;
     put ( "s3C: " ); put( stringsC(3).all ); New_line;
   end;
end raggedtest;

with raggedtest; use raggedtest;
procedure main is
begin
   mytest;
end main;
resander
  • 1,181
  • 2
  • 11
  • 15