Discussion:
Ada and string literals
(too old to reply)
codeallergy
2013-01-30 00:44:44 UTC
Permalink
hi comp.lang.ada,

question from a newcomer: Why ada does not allow using a string literal with access type ?

abc : access string := "LITERAL"; -- error.

example with C: http://ideone.com/Ioapww

Thanks

Rom B.
Niklas Holsti
2013-01-30 07:08:12 UTC
Permalink
Post by codeallergy
hi comp.lang.ada,
question from a newcomer: Why ada does not allow using a string literal with access type ?
abc : access string := "LITERAL"; -- error.
Because, unlike C, Ada does not confuse arrays with pointers.

This is the closest Ada equivalent:

Literal : aliased constant String := "LITERAL";
abc : access constant String := Literal'Access;
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
Mart van de Wege
2013-01-30 11:50:38 UTC
Permalink
Post by Niklas Holsti
Post by codeallergy
hi comp.lang.ada,
question from a newcomer: Why ada does not allow using a string
literal with access type ?
abc : access string := "LITERAL"; -- error.
Because, unlike C, Ada does not confuse arrays with pointers.
Literal : aliased constant String := "LITERAL";
abc : access constant String := Literal'Access;
More importantly, I cannot see why you would want an access to a string
literal.

Just use the string literal as a normal parameter, and let the compiler
worry about how to handle that.

Mart
--
"We will need a longer wall when the revolution comes."
--- AJS, quoting an uncertain source.
Niklas Holsti
2013-01-30 13:52:58 UTC
Permalink
Post by Mart van de Wege
Post by Niklas Holsti
Post by codeallergy
hi comp.lang.ada,
question from a newcomer: Why ada does not allow using a string
literal with access type ?
abc : access string := "LITERAL"; -- error.
Because, unlike C, Ada does not confuse arrays with pointers.
Literal : aliased constant String := "LITERAL";
abc : access constant String := Literal'Access;
More importantly, I cannot see why you would want an access to a string
literal.
Just use the string literal as a normal parameter, and let the compiler
worry about how to handle that.
I don't know why the OP wants an access, but I can image a situation
where one needs, for example, to call a subprogram with several string
parameters, which should have different values depending on different
logical conditions, in various combinations. It is then easier to
represent each parameter by local variable that can be assigned
different values in different if-then-else statements, and then use the
local variables as actual parameters in the call.

For example, assume you must call a procedure Foo (A, B: in String), and
A can be either "James" or "John" depending on condition Alpha, while B
can be either "Jill" or "Jennie" depending on condition Beta. You would
like to write something like this:

-- The following does NOT work:
..
is
A, B : String;
begin
if Alpha then A := "James"; else A := "John" ; end if;
if Beta then B := "Jill"; else B := "Jennie"; end if;
Foo (A, B);
..

That doesn't work, because the declaration of A and B must specify a
length for the string, and then you cannot assign literal strings of
different length to A and B.

If you use string literals directly as parameters, you need different
calls for all combinations:

if Alpha then
if Beta then
Foo ("James", "Jill");
else
Foo ("James", "Jennie");

and so on, for a combinatorial number of cases.

Of course you can use Ada.Strings.Unbounded or Ada.Strings.Bounded,
which let you assign strings of different lengths to the same variable,
but accesses to strings are another solution.

In the above simple example one could also use Ada 2012 conditional
expressions:

Foo (A => (if Alpha then "James" else "John" ),
B => (if Beta then "Jill" else "Jennie"));

but in more complex cases that no longer works, or not as well as
assigning the actual parameter values to local variables before the call.
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
Adam Beneschan
2013-01-30 16:09:13 UTC
Permalink
Post by Niklas Holsti
Post by Mart van de Wege
More importantly, I cannot see why you would want an access to a string
literal.
Just use the string literal as a normal parameter, and let the compiler
worry about how to handle that.
I don't know why the OP wants an access, but I can image a situation
where one needs, for example, to call a subprogram with several string
parameters, which should have different values depending on different
logical conditions, in various combinations. It is then easier to
represent each parameter by local variable that can be assigned
different values in different if-then-else statements, and then use the
local variables as actual parameters in the call....
Another fairly common use case is a constant array whose values are string literals.

By the way, if you use an "access constant string" type, I believe that compilers should, if possible, work things out so that if the type of

new string' ("Literal")

is an access-constant-string, the compiler will generate a "literal" pointer that points to constant data loaded together with the code, so that the NEW won't cause any allocation or any other code to be run at runtime. This isn't a requirement, but it's recommended. That would result in something equivalent to the situation in C, without any unnecessary overhead. You'd have to make sure you don't try to convert this access type to an access-[not-constant]-string type, though; that isn't allowed for obvious reasons.

-- Adam
Shark8
2013-02-01 00:54:54 UTC
Permalink
Post by Niklas Holsti
..
is
A, B : String;
begin
if Alpha then A := "James"; else A := "John" ; end if;
if Beta then B := "Jill"; else B := "Jennie"; end if;
Foo (A, B);
..
But it's unneeded in Ada 2012 -- use the following:
Foo(
Param_1 => (if alpha then "James" else "John"),
Param_2 => (if beta then "Jill" else "Jennie")
);
Niklas Holsti
2013-02-01 09:03:38 UTC
Permalink
Post by Shark8
Post by Niklas Holsti
..
is
A, B : String;
begin
if Alpha then A := "James"; else A := "John" ; end if;
if Beta then B := "Jill"; else B := "Jennie"; end if;
Foo (A, B);
..
Foo(
Param_1 => (if alpha then "James" else "John"),
Param_2 => (if beta then "Jill" else "Jennie")
);
I guess you didn't read to the end of my post before replying.

Using Ada 2012 conditional expressions in the actual parameters works in
this simple example, but become cumbersome (e.g. because of duplicated
conditions and nested conditions) in more complex cases.
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
Shark8
2013-02-01 14:58:52 UTC
Permalink
Post by Niklas Holsti
I guess you didn't read to the end of my post before replying.
Guilty.
I've been writing up some tutorials for Ada 2012 and, like an overexcited schoolboy, thought "I know this, I know this!" in the middle of the post.

*insert embarrassed smile*
Sorry about that.
Post by Niklas Holsti
Using Ada 2012 conditional expressions in the actual parameters works in
this simple example, but become cumbersome (e.g. because of duplicated
conditions and nested conditions) in more complex cases.
True, though you can somewhat mitigate that with some structuring.
codeallergy
2013-01-30 16:52:32 UTC
Permalink
Okay. assuming dat we use GNAT 2012

Literal1 : aliased constant String := "LITERAL";
Literal2 : aliased constant String := "LITERAL";
Literal3 : aliased constant String := "LITERAL";

how the above code is handled ?

compiler duplicate the string three times into the data section or
the three constants will share the same string or
one string into the data section but duplicated at the run time ?



another question: how free a object from a procedure ?

example:
procedure Free_Abc (Target : access String)
is
begin
GNAT.Strings.Free(Target); -- error
end Free_Abc;

this code produce the error: actual for "X" must be a variable

why disallow that ?

thanks,
Rom B.
Adam Beneschan
2013-01-30 17:19:17 UTC
Permalink
Post by codeallergy
Okay. assuming dat we use GNAT 2012
Literal1 : aliased constant String := "LITERAL";
Literal2 : aliased constant String := "LITERAL";
Literal3 : aliased constant String := "LITERAL";
how the above code is handled ?
compiler duplicate the string three times into the data section or
the three constants will share the same string or
one string into the data section but duplicated at the run time ?
I don't know how GNAT handles it; but in Ada, having the string objects share the same memory causes a problem if the program later compares the 'Access of two different objects. Ada says they're supposed to be unequal, but I think that would be hard to do if the string literals shared the same memory. (I can think of ways to do this by adding some additional data to the access values, but that could easily result in way more overhead than just duplicating the strings would take up.)
Post by codeallergy
another question: how free a object from a procedure ?
procedure Free_Abc (Target : access String)
is
begin
GNAT.Strings.Free(Target); -- error
end Free_Abc;
this code produce the error: actual for "X" must be a variable
why disallow that ?
Because the definition of Free is that you give it an access variable, it frees the storage, then it sets the variable to null. Target is basically a read-only value in this procedure, so you can't set it to null. You can get around it easily enough by declaring a Temp variable and then

Temp := Target;
GNAT.Strings.Free(Temp);

But then whoever called Free_Abc may still have a variable that points to a string that no longer exists. So you have to be careful. I don't really recommend having a procedure such as Free_Abc that frees storage and isn't able to set the access to null; better to define a *named* access type that is access-to-String and make it an IN OUT parameter to Free_Abc.

-- Adam
Simon Wright
2013-01-30 20:02:34 UTC
Permalink
Post by codeallergy
Okay. assuming dat we use GNAT 2012
Literal1 : aliased constant String := "LITERAL";
Literal2 : aliased constant String := "LITERAL";
Literal3 : aliased constant String := "LITERAL";
how the above code is handled ?
compiler duplicate the string three times into the data section or the
three constants will share the same string or one string into the data
section but duplicated at the run time ?
There are three occurences of the string in the data section.

Of course, there may be some GCC option to merge the strings - there's
probably an option to make toast, come to that.
Georg Bauhaus
2013-01-30 20:19:15 UTC
Permalink
Post by Simon Wright
Of course, there may be some GCC option to merge the strings - there's
probably an option to make toast, come to that.
Yes, GCC burns enough CPU cycles for making toast when a
mini C99 program uses larger variable length arrays of C99.
I'm building GCC from latests sources hoping I won't need to
report the ICEs I'm seeing on this hard working system.
Robert A Duff
2013-01-30 22:06:46 UTC
Permalink
Post by codeallergy
Okay. assuming dat we use GNAT 2012
Literal1 : aliased constant String := "LITERAL";
Literal2 : aliased constant String := "LITERAL";
Literal3 : aliased constant String := "LITERAL";
how the above code is handled ?
compiler duplicate the string three times into the data section or
Yes, that's the most likely implementation.
Post by codeallergy
the three constants will share the same string or
That won't work, because Literal1 = Literal2 must be False.
If you leave out "aliased", then the compiler might share.
Post by codeallergy
one string into the data section but duplicated at the run time ?
That would work, but I don't see why a compiler would do that.

- Bob
Jeffrey Carter
2013-01-30 22:10:56 UTC
Permalink
Post by Robert A Duff
That won't work, because Literal1 = Literal2 must be False.
If you leave out "aliased", then the compiler might share.
I think there are some attributes missing here.
--
Jeff Carter
"I was hobbling along, minding my own business, all of a
sudden, up he comes, cures me! One minute I'm a leper with
a trade, next minute my livelihood's gone! Not so much as a
'by your leave!' You're cured, mate. Bloody do-gooder!"
Monty Python's Life of Brian
76
Robert A Duff
2013-01-31 02:23:08 UTC
Permalink
Post by Jeffrey Carter
Post by Robert A Duff
That won't work, because Literal1 = Literal2 must be False.
If you leave out "aliased", then the compiler might share.
I think there are some attributes missing here.
Oops. Thanks!

Yes, I meant that Literal1'Access = Literal2'Access must be False.
Same for 'Unchecked_Access.

'[Unchecked_]Access is about object identity.
On the other hand 'Address is not -- two objects can have
the same address without being the same object.

- Bob
Adam Beneschan
2013-01-31 15:49:29 UTC
Permalink
Post by Robert A Duff
'[Unchecked_]Access is about object identity.
On the other hand 'Address is not -- two objects can have
the same address without being the same object.
However, if two objects have the same 'Address, it's hard to imagine them not having the same 'Access as well, in practice. There aren't many cases where this is an issue, but:

procedure P (Addr : in System.Address) is

X : aliased T;
for X'Address use Addr;
pragma Import (Ada, X);

Y : aliased T;
for Y'Address use Addr;
pragma Import (Ada, Y);

XA : T_Acc := X'Access;
YA : T_Acc := Y'Access;

...

if XA = YA then ...

end Proc;

Assuming T is a simple record type, for instance, I can't imagine XA=YA being false in any implementation, even though I believe X and Y are distinct objects by the RM's definitions. The additional logic and/or storage required to make sure XA /= YA would be unacceptable distributed overhead.

(This may or may not apply to unconstrained String subtypes such as in the example; if an implementation already uses an implicit level of indirection somewhere to implement those, then it may be possible that 'Address would be the same and 'Access different without any additional overhead.)

-- Adam
Robert A Duff
2013-01-31 22:24:22 UTC
Permalink
Post by Adam Beneschan
Post by Robert A Duff
'[Unchecked_]Access is about object identity.
On the other hand 'Address is not -- two objects can have
the same address without being the same object.
However, if two objects have the same 'Address, it's hard to imagine
them not having the same 'Access as well, in practice.
It's easy to imagine that 'Access is illegal. ;-) That was part of my
point: a compiler can place two objects at the same address if
they are not aliased. For example, if one of them has zero size.
And I wasn't thinking about the case where the user FORCES the
same address as in your example below.
Post by Adam Beneschan
procedure P (Addr : in System.Address) is
X : aliased T;
for X'Address use Addr;
pragma Import (Ada, X);
...
Post by Adam Beneschan
Assuming T is a simple record type, for instance, I can't imagine
XA=YA being false in any implementation, even though I believe X and Y
are distinct objects by the RM's definitions.
For sure. Are X and Y distinct objects? I'd say the correct
answer is "Who cares?" ;-) Maybe this is a bug in the RM,
but I hope nobody pesters the ARG about it. IMHO, bugs in the
RM should be reported to ARG only if fixing them would change some
compiler writer's behavior, or change some programmer's behavior.

- Bob
g***@hotmail.com
2013-02-01 21:16:53 UTC
Permalink
Post by codeallergy
another question: how free a object from a procedure ?
with Ada.Unchecked_Deallocation;
...
procedure Free is new Ada.Unchecked_Deallocation(String, String_Access);
Post by codeallergy
procedure Free_Abc (Target : access String)
is
begin
GNAT.Strings.Free(Target); -- error
end Free_Abc;
this code produce the error: actual for "X" must be a variable
why disallow that ?
The access parameter seems to be like an "in" parameter and treated as a local constant (only a guess, I don't use access parameters).
What you pass as an "in" parameter is not certainly a variable
You could have a call like Free_Abc(null);
Similarly you could have

procedure Do_something(x: in Integer) is
begin
x:= x + 1; -- same error
end;

But with the "in" parameter, you could have a call like
Do_something(1234);
which makes the x:= x + 1 nonsensical.

Cheers
Gautier
Stephen Leake
2013-02-02 01:55:06 UTC
Permalink
Post by codeallergy
another question: how free a object from a procedure ?
procedure Free_Abc (Target : access String)
is
begin
GNAT.Strings.Free(Target); -- error
end Free_Abc;
this code produce the error: actual for "X" must be a variable
why disallow that ?
Because Free sets its argument to null, which it can't do if it's not a
variable.

Setting the argument to null helps avoid dangling pointers.
--
-- Stephe
Robert A Duff
2013-02-02 14:30:37 UTC
Permalink
Post by Stephen Leake
Setting the argument to null helps avoid dangling pointers.
It does. But I doubt it helps a lot, because it only detects
the most obvious case, where you free X and then refer to
X.all. The more subtle bug is when you free X and then refer
to Y.all (or free Y). Setting X to null is no help there.

- Bob

a***@gmail.com
2013-01-31 09:20:41 UTC
Permalink
The other day I was looking at the Ada code for the GPS (GNAT Programming Studio). This is code taken from the procedure GPS.Main:

subtype String_Access is GNAT.Strings.String_Access;

Home : String_Access;
Home_Dir : Virtual_File;
Project_Name : Virtual_File := No_File;
Prefix : String_Access;
Prefix_Dir : Virtual_File;
GPS_Home_Dir : Virtual_File;
Batch_File : String_Access;
Batch_Script : String_Access;
Tools_Host : String_Access;
Target : String_Access;
Protocol : String_Access;
Debugger_Name : String_Access;
Startup_Dir : String_Access;
About_Contents : String_Access;

The string access type was chosen over the string type for handling strings. Maybe to avoid setting a maximum string length? Performance reasons? Anyways, I think the authors of the GPS are able to give a good motivation of when one prefers the string access type over the ordinary string type.

Best regards,
Åke Ragnar Dahlgren
Robert A Duff
2013-01-30 16:20:17 UTC
Permalink
Post by Niklas Holsti
Post by codeallergy
hi comp.lang.ada,
question from a newcomer: Why ada does not allow using a string
literal with access type ?
abc : access string := "LITERAL"; -- error.
Because, unlike C, Ada does not confuse arrays with pointers.
Literal : aliased constant String := "LITERAL";
abc : access constant String := Literal'Access;
That works, but this seems like a closer equivalent to me:

Abc : access constant String := new String'("LITERAL");

Ada ought to allow:

Abc : access constant String := "LITERAL"'Access;

with or without the "constant", as a shorthand for the above.

Suppose you want an array of strings. Well, you can't have
one in Ada, except in the unusual case where they all happen
to have the same length. So you use array of access to string.

Colors : constant String_Sequence :=
("red"'Access, -- This is not Ada!
"orange"'Access,
"dark green"'Access,
...);

That gets pretty tedious if you declare a name for each color.

I'd allow arrays of Strings, though, but that's even less Ada-like.

- Bob
Stephen Leake
2013-02-01 13:20:24 UTC
Permalink
Post by Robert A Duff
Suppose you want an array of strings. Well, you can't have
one in Ada, except in the unusual case where they all happen
to have the same length. So you use array of access to string.
Colors : constant String_Sequence :=
("red"'Access, -- This is not Ada!
"orange"'Access,
"dark green"'Access,
...);
That gets pretty tedious if you declare a name for each color.
That's what "+" is for:

function "+" (Item : in String) return access constant String
is begin
return new String'(Item);
end "+";

Colors : constant String_Sequence :=
(+"red", -- This _is_ Ada!
+"orange",
+"dark green",
...);

I would be _very_ nice if that definition of "+" was in Ada.Strings
somewhere.

Of course, if you also want the non-constant equivalent:

function "+" (Item : in String) return access String

then can you have ambiguity problems.
--
-- Stephe
Robert A Duff
2013-02-01 14:49:27 UTC
Permalink
Yeah, I use that technique sometimes. But it feels more like
a "workaround" than a "technique".
Post by Stephen Leake
I would be _very_ nice if that definition of "+" was in Ada.Strings
somewhere.
It would be even nicer if you could declare an array-of-String.

- Bob
Dmitry A. Kazakov
2013-02-01 17:23:00 UTC
Permalink
Post by Stephen Leake
function "+" (Item : in String) return access constant String
is begin
return new String'(Item);
end "+";
Colors : constant String_Sequence :=
(+"red", -- This _is_ Ada!
+"orange",
+"dark green",
...);
What about:

function "&" (Left, Right : String) return String_Sequence is
begin
return (new String'(Left), new String'(Right));
end "&";

function "&" (Left : String; Right : String_Sequence)
return String_Sequence is
begin
return String_Sequence'(1 => new String'(Left)) & Right;
end "&";

Colors : constant String_Sequence := "red" & "orange" & "dark green";
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
Robert A Duff
2013-02-01 20:22:09 UTC
Permalink
Post by Dmitry A. Kazakov
Post by Stephen Leake
function "+" (Item : in String) return access constant String
is begin
return new String'(Item);
end "+";
Colors : constant String_Sequence :=
(+"red", -- This _is_ Ada!
+"orange",
+"dark green",
...);
function "&" (Left, Right : String) return String_Sequence is
begin
return (new String'(Left), new String'(Right));
end "&";
function "&" (Left : String; Right : String_Sequence)
return String_Sequence is
begin
return String_Sequence'(1 => new String'(Left)) & Right;
end "&";
Colors : constant String_Sequence := "red" & "orange" & "dark green";
Looks dangerous to me. If I'm not mistaken, you have a bug: ;-)
the above sets Colors to a 2-element array containing a pointer
to "redorange" and a pointer to "dark green". The first "&" in Colors
calls the predefined String one, and the second "&" calls the first
"&" body shown above. The second body is never called.

I suppose that could be fixed, but we're getting further and
further from something where the compiler is likely to generate
a statically-allocated table of pointers to statically-allocated
strings.

- Bob
Dmitry A. Kazakov
2013-02-01 22:03:43 UTC
Permalink
Post by Robert A Duff
Post by Dmitry A. Kazakov
Post by Stephen Leake
function "+" (Item : in String) return access constant String
is begin
return new String'(Item);
end "+";
Colors : constant String_Sequence :=
(+"red", -- This _is_ Ada!
+"orange",
+"dark green",
...);
function "&" (Left, Right : String) return String_Sequence is
begin
return (new String'(Left), new String'(Right));
end "&";
function "&" (Left : String; Right : String_Sequence)
return String_Sequence is
begin
return String_Sequence'(1 => new String'(Left)) & Right;
end "&";
Colors : constant String_Sequence := "red" & "orange" & "dark green";
Looks dangerous to me. If I'm not mistaken, you have a bug: ;-)
the above sets Colors to a 2-element array containing a pointer
to "redorange" and a pointer to "dark green". The first "&" in Colors
calls the predefined String one, and the second "&" calls the first
"&" body shown above. The second body is never called.
We could use "/" or "+" instead of "&".

Colors : constant String_Sequence := "red" / "orange" / "dark green";

(still better than ugly prefix "+")
Post by Robert A Duff
I suppose that could be fixed, but we're getting further and
further from something where the compiler is likely to generate
a statically-allocated table of pointers to statically-allocated
strings.
Which is not likely the proper way for the problem at hand, anyway. I guess
that the solution for what OP is supposedly doing would be an initialized
map or table with string keys. Such things are initialized in the package
body using "begin ... end" construct.

It would be very helpful if Ada allowed that through user-defined static
operations, but that is a story for another day.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
codeallergy
2013-01-30 22:54:01 UTC
Permalink
Thanks for answering.

i reversed the generated code. when u use a literal as a argument like this

Put_Line("HELLO");
Abc("HELLO");

the same string from the data section is shared. when you use constants from a package like this

package Abc is
str1 : aliased constant String : "LITERAL";
str2 : aliased constant String : "LITERAL";
end Abc;

strings are duplicated and put into the data section, aliased or not. when you use constant from a procedure header (dunno how u name that) like this

procedure Abc
is
str1 : aliased constant String : "LITERAL";
begin
null;
end Abc;

if the constant is aliased, the constant is not put into the data section but "hardcoded" and push on the stackframe like this:

.text:00401FA2 mov dword ptr [ebp-104h], 1
.text:00401FAC mov dword ptr [ebp-100h], 7
.text:00401FB6 mov dword ptr [ebp-0FCh], 4554494Ch ; 4C49544552414C = "LITERAL"
.text:00401FC0 mov word ptr [ebp-0F8h], 4152h
.text:00401FC9 mov byte ptr [ebp-0F6h], 4Ch

if not aliased the constant is put into the data section.

- Rom B.
Shark8
2013-02-01 00:50:35 UTC
Permalink
Post by codeallergy
hi comp.lang.ada,
question from a newcomer: Why ada does not allow using a string literal with access type ?
abc : access string := "LITERAL"; -- error.
example with C: http://ideone.com/Ioapww
Thanks
Rom B.
Because a String and an Access String are different types.
You could fix it by saying:
abc : access string := new String'("LITERAL");
or perhaps more safely:
def : Aliased String:= "LITERAL";
abc : access string := def'access;
Continue reading on narkive:
Loading...