[comp.lang.ada] C Strings in Ada?

defaria@hpclapd.HP.COM (Andy DeFaria) (06/05/90)

I would like to create a small package that interfaces to some C code via a
pragma INTERFACE and I have a question to all you Ada pros out there.  What
is the best way to represent C_Strings in Ada?  C's notion of a string is a
pointer to a  char of undetermined length, terminated  by a null character.
Ada has a  totally  different notion of  what  a string is.   I  can  see 3
instances where I need to use a "C_String":

	1) As parameters to a subprogram call.
	2) As a value returned from a function call.
	3) As a part of a record passed to (or returned by) a function.

I am concerned  with usability  and  efficiency as I  intend to reuse  this
package.  I  could  declare an  Ada  string  and pass the 'ADDRESS   to the
interfaced C  code  but then  I  need to append the   null character before
calling  the routine in  the package.  I could have  the package append the
null character but this would require one or two heap allocations  on every
call  and  return which I  consider a big drawback  from  the  viewpoint of
efficiency.  I have tried defining this "C_String" as it is described to C,
as an access to CHARACTER but  with this representation  I can  not look at
the  value of the Ada string  in the debugger  because the debugger faithly
shows me only the first character.  I consider this a big drawback from the
viewpoint of usability.  And how should  strings be  described as part of a
record structure?

I can see three possiblities, none of which are ideal:

(1) Represent C strings as access types to STANDARD.CHARACTER

The pointer points to a  null terminated string.  Since this  uses the same
representation as C no conversions  are  necessary.   I would  then have to
provide subprograms to convert regular Ada strings into this data type.

Advantages: 	Speed, Efficiency
Disadvantages:	Only the first character is visible in the debugger,  
		Need to call the conversion routines before making the call
		the  package.   No conversion  is  needed if  the parameter
		itself was obtained   from a  previous call  to  one  of my
		routines in the package or if the  same value was used as a
		paramater from a previous call.

(2) Represent the strings as Ada strings

In this scheme, the caller would  use only Ada  strings and my package will
always perform the conversion of the string into  a null terminated string.
  

Advantages:	Ada-like, only one interpretation of a "string" needs to be
		considered     by    the    users      of     the  package.
Disadvantanges:	The  heap manager has   to be called  not  only to allocate
		strings, but  also  records that  contain  strings.   Since
		there  are now C  versions of records  and Ada versions  of
		records, a subprogram should  be provided  to convert the C
		version of  the record to  the Ada version  of  the record.
		Also messier to implement and  users of my package may come
		upon the C version of the record by chasing pointers within
		a record.

(3) Represent the C strings as Ada strings, but ...

In this scheme,  the caller of my package  should use Ada strings that  are
terminated with the null character thereby placing  some  responisbility on
them.  My package can  then pass these  strings directly to  the C routines
I'm interfacing to.  If I discover that the caller was lazy and shucked his
responsibility by not terminating the string  with a null character, then I
can do  it for them  and thereby incur the   overhead of calling  the  heap
manager only  if  the caller  forgot to terminate  the  string with a null.
When my package returns character  strings, then  I  would have to allocate
space for these strings by calling the heap  manager but I  would be giving
the caller a string that they can in turn pass back  to  my package without
additional heap manager overhead.

Advantages:	Compromise  between the  two other method  presented.  Heap
		manager overhead  is  incurred only   when  necessary.   If
		strings that are contained in record structures are defined
		as access to Ada strings then the records  are of identical
		size.

Disadvantages:	Does   not solve  the problem of  strings  contained within
		records.  Since there is a C  version  of the record and an
		Ada version,   I  still have   the   problems of  providing
		conversion routines.

4) Combine methods 1 & 2.

This  scheme  would   basically combine    methods 1 &  2 by    using Ada's
overloaded function  call  capability.   Thus the  caller  could use either
method. 

Advantages:	Caller could  use the  Ada-like calls while  debugging  and
		migrate to the faster C-like calls.

Disadvantages:	Much more   work involved for   me in writing  the package.
		Still have a problem with strings inside of  records.  Once
		the user migrates to the faster  C-like calls it is hard to
		debug because they can't look at any "C_Strings".  Requires
		that  they change  their   code to  use  "Ada Strings"  and
		recompile.

What do you think?  How would you implement "C_Strings" in Ada?

stt@inmet.inmet.com (06/08/90)

Re: C_Strings in Ada

The simplest way to turn an Ada string into a string acceptable to C is:
  STR & ASCII.NUL
This works quite well in several compilers, and only
requires that the called C code ignore the additional
array descriptor often passed along with an unconstrained array.

On the other hand, if you want a type which you can manipulate
in Ada which represents a null-terminated string of unknown length,
it is best to define an access type to a *constrained* string subtype.
You can then pretty safely use unchecked conversion
to convert the address of the C string into a value of this access type.
You may then refer to the characters of the C string so long
as the C string does not exceed the length of the constrained
string subtype.  For instance:

    type C_String_Ptr is access String(1..Integer'LAST);
    function To_C_String_Ptr is new Unchecked_Conversion(
      System.Address, C_String_Ptr
    );
    Addr : System.Address;
    Ptr : C_String_Ptr;
begin
    Addr := <address of C-string>
    Ptr := To_C_String_Ptr(Addr);
    for I in Ptr'RANGE loop
        exit when Ptr(I) = ASCII.NUL;
        <do something with Ptr(I)>
    end loop;
 
If you use unchecked conversion between an address and
an access to an unconstrained array type, on many
compilers all hell breaks loose, because the access value
is assumed to be pointing at a descriptor, but you
have set it to point directly at data.

BTW, None of this works on a RATIONAL, at least not until
they get their C++ compiler working :-*.

S. Tucker Taft
Intermetrics, Inc.
Cambridge, MA  02138

defaria@hpclapd.HP.COM (Andy DeFaria) (06/08/90)

>/ hpclapd:comp.lang.ada / emery@D74SUN.MITRE.ORG (David Emery) / 10:21 am  Jun  6, 1990 /
>>What is the best way to represent C_Strings in Ada?  
>
>The right solution is to keep all strings in the Ada program as Ada
>strings, and change them to/from C strings at the point of interface
>to C.  (i.e.  within the body of Ada_routine_calling_c).  This
>preserves the Ada abstractions, and makes all the Ada things work just
>fine (e.g. 'IMAGE, which returns an Ada string).  The only other
>alternative (that makes sense to me) is to make C_STRING a private
>type derived from System.address, but you won't be able to do anything
>useful in the debugger with this.
>
>How you do this conversion is very machine dependent, but here's my
>tricks that work on Verdix (Sun 3) and Meridian (IBM PC):
>
>	procedure to_c (str : string) 
>	is
>	  procedure c_routine (addr : system.address);
>          pragma interface (C, c_routine);	-- (void) c_routine(s)
>						-- char * s;
>	  c_string : constant string := str & Ascii.NUL;
>	begin
>	  c_routine(c_string(c_string'first)'address);
>	end to_c;
>
>	function from_c  
>	    return string
>	is
>	    function c_function			
>		return System.address;		
>	    pragma interface (C, c_function);	-- char * c_function
>	    function strlen (addr : System.address)
>		return integer;
>	    pragma interface (C, strlen);	-- the C function of
>						-- the same name
>	    addr_from_c : System.address;
>	 begin
>	    addr_from_c := c_function;
>	    declare
>	      answer : string(1..strlen(addr_from_c);
>	      for answer'address use at addr_from_c;
>	    begin
>	      return answer;
>	    end;
>  	 end from_c;
>
>Hopefully what's going here is obvious, if not let me know and I'll
>send more information.
>
>				dave emery
>				emery@aries.mitre.org
>----------

I kinda agree with you that the "right" way to do this would be to keep Ada
strings as  Ada strings and do the  conversion  in package but you have not
addressed one of my critical concerns: What about  the overhead incurred by
having  to call the  heap manager to allocate a  new string just  to append
ASCII.NUL so  that the C routine I'm   calling  will be  able to handle  it
properly.   I anticipate  that this overhead will have  an severe impact on
performance and Ada already has a bad rap for being a dog.  I don't want to
add to that reputation.

Another, problem that you haven't addressed  is that  of the representation
of a string in a record.  Should it be  and  Ada access to string or should
it be the string itself.  Or should it be a C string (char *)?

blakemor@software.org (Alex Blakemore) (06/11/90)

In article <920025@hpclapd.HP.COM> defaria@hpclapd.HP.COM (Andy DeFaria) writes:
>What is the best way to represent C_Strings in Ada?  

Dave Emery presented the following solution for some compilers.

$$	procedure to_c (str : string) 
$$	is
$$	  procedure c_routine (addr : system.address);
$$          pragma interface (C, c_routine);	-- (void) c_routine(s)
$$						-- char * s;
$$	  c_string : constant string := str & Ascii.NUL;
$$	begin
$$	  c_routine(c_string(c_string'first)'address);
$$	end to_c;
$$
$$	function from_c  
$$	    return string
$$	is
$$	    function c_function			
$$		return System.address;		
$$	    pragma interface (C, c_function);	-- char * c_function
$$	    function strlen (addr : System.address)
$$		return integer;
$$	    pragma interface (C, strlen);	-- the C function of
$$						-- the same name
$$	    addr_from_c : System.address;
$$	 begin
$$	    addr_from_c := c_function;
$$	    declare
$$	      answer : string(1..strlen(addr_from_c);
$$	      for answer'address use at addr_from_c;
$$	    begin
$$	      return answer;
$$	    end;
$$  	 end from_c;

Mr. DeFaria replied
> I kinda agree with you that the "right" way to do this would be to keep Ada
> strings as  Ada strings and do the  conversion  in package but you have not
> addressed one of my critical concerns: What about  the overhead incurred by
> having  to call the  heap manager to allocate a  new string just  to append
> ASCII.NUL so  that the C routine I'm   calling  will be  able to handle  it

I dont think this solution *requires* heap allocation.  A good compiler would 
be free to use stack space for the C strings in these cases, both for
the local constant c_string and for the return value.  Of course, there is 
the overhead of copying the strings but the allocation can be relatively
cheap.

-----------------------------------------------------------------------
Alex Blakemore                       Internet: blakemore@software.org
Software Productivity Consortium     UUNET:    ...!uunet!software!blakemore
2214 Rock Hill Rd, Herndon VA 22070  Bellnet:  (703) 742-7125

adoyle@bbn.com (Allan Doyle) (06/11/90)

Whew, the nesting is getting too deep. I sliced out the parts I want to
comment on...

>>What is the best way to represent C_Strings in Ada?  

>$$	procedure to_c (str : string) 
>$$	is
>$$	  procedure c_routine (addr : system.address);
>$$          pragma interface (C, c_routine);	-- (void) c_routine(s)
>$$						-- char * s;
>$$	  c_string : constant string := str & Ascii.NUL;
>$$	begin
>$$	  c_routine(c_string(c_string'first)'address);
>$$	end to_c;

>I dont think this solution *requires* heap allocation.  A good compiler would 
>be free to use stack space for the C strings in these cases, both for
>the local constant c_string and for the return value.  Of course, there is 
>the overhead of copying the strings but the allocation can be relatively
>cheap.
>
>-----------------------------------------------------------------------
>Alex Blakemore                       Internet: blakemore@software.org
>Software Productivity Consortium     UUNET:    ...!uunet!software!blakemore
>2214 Rock Hill Rd, Herndon VA 22070  Bellnet:  (703) 742-7125

Judging from the replies about heap space, I'm getting a little worried.
Do you guys mean to tell me that it's compiler dependent how to allocate
the 'c_string' constant? This is just the sort of thing that makes me
mistrust Ada for a realtime application that has to run for a long time
without any memory leaks. If I take the worst case assumption that c_string
is being allocated from the heap and add it to the worst case assumption
that my Ada run-time will not be doing garbage collection (perfectly legal,
I understand), then how many of these little beasties can I convert from
Ada to C before I run out of memory?



Allan Doyle                                        adoyle@bbn.com
BBN Systems and Technologies Corporation           (617) 873-3398
70 Fawcett Street,   Cambridge, MA 02138

kassover@minerva.crd.ge.com (David Kassover) (06/11/90)

In article <57245@bbn.BBN.COM> adoyle@vax.bbn.com (Allan Doyle) writes:
...
>
>Judging from the replies about heap space, I'm getting a little worried.
>Do you guys mean to tell me that it's compiler dependent how to allocate
>the 'c_string' constant? This is just the sort of thing that makes me
>mistrust Ada for a realtime application that has to run for a long time
>without any memory leaks. If I take the worst case assumption that c_string
>is being allocated from the heap and add it to the worst case assumption
>that my Ada run-time will not be doing garbage collection (perfectly legal,
>I understand), then how many of these little beasties can I convert from
>Ada to C before I run out of memory?

Perhaps I'm naive.  But isn't management of things like heaps and
stacks and stuff the purview of the underlying hardware and
operating system (if any?)

Under VMS, regardless of language used (well, take my absolutes
as approximately equal to .9944  8-), heap, stack, vm, etc
management can be affected by several parameters pertaining to
the operating system, the user account structure, and the current
hardware configuration.

On the other hand, if you *have* no operating system (your
program lives in an automated teller machine, e.g.) then when you
run out of memory depends on how much you have and how busy your
program was.  If this is a problem, then you must write a garbage
collector yourself (and find room for it in memory  8-), or get
the powers that be to plug in a bigger memory board, or poll the
ATM more frequently.

And furthermore, if what you are writing *is* an operating system
(in the sense of, say, OS 360, MS-DOS, VAX-VMS, VM/CMS, Primos,
or bhog-knows-what), then you *also* might consider writing a
garbage collector, and publishing instructions to writers of
compilers on how to take advantage of it.

But that has nothing, IMHO, to do with Ada.

Or have I missed a point here?
--
David Kassover             "Proper technique helps protect you against
kassover@ra.crd.ge.com	    sharp weapons and dull judges."
kassover@crd.ge.com			F. Collins

emery@linus.mitre.org (David Emery) (06/12/90)

Allan Doyle writes
>If I take the worst case assumption that c_string
>is being allocated from the heap and add it to the worst case assumption
>that my Ada run-time will not be doing garbage collection (perfectly legal,
>I understand), then how many of these little beasties can I convert from
>Ada to C before I run out of memory?

It's not clear to me that you can guarantee with a C compiler that
there is not some compiler-generated heap, either.  Does the C
LANGUAGE guarantee non-heap allocation for a local variable???    Or
does "everyone knows that's how it's done?"

 				dave emery
				emery@aries.mitre.org

adoyle@bbn.com (Allan Doyle) (06/12/90)

In article <8446@crdgw1.crd.ge.com>
 kassover@minerva.crd.ge.com (David Kassover) writes:
>In article <57245@bbn.BBN.COM> adoyle@vax.bbn.com (Allan Doyle) writes:
>...
>>
>>Judging from the replies about heap space, I'm getting a little worried.
>>Do you guys mean to tell me that it's compiler dependent how to allocate
>>the 'c_string' constant? This is just the sort of thing that makes me
>>mistrust Ada for a realtime application that has to run for a long time
>>without any memory leaks. If I take the worst case assumption that c_string
>>is being allocated from the heap and add it to the worst case assumption
>>that my Ada run-time will not be doing garbage collection (perfectly legal,
>>I understand), then how many of these little beasties can I convert from
>>Ada to C before I run out of memory?
>
>Perhaps I'm naive.  But isn't management of things like heaps and
>stacks and stuff the purview of the underlying hardware and
>operating system (if any?)
>
>Under VMS, regardless of language used (well, take my absolutes
>as approximately equal to .9944  8-), heap, stack, vm, etc
>management can be affected by several parameters pertaining to
>the operating system, the user account structure, and the current
>hardware configuration.
>
>--
>David Kassover             "Proper technique helps protect you against
>kassover@ra.crd.ge.com	    sharp weapons and dull judges."
>kassover@crd.ge.com			F. Collins

Well, maybe the OS is responsible for managing memory at a macro level, but
certainly not at the level of a few bytes here and there for the kind
of strings we're talking about. The Ada runtime will probably do malloc()
for some hunk of memory when it needs more heap space, and will probably
do a free() call of some sort when it thinks it's done with a large hunk
of memory (but the LRM says it does not have to do so, or at least does
not mention it at all). But if the runtime does not do garbage collection
when my program is done with a few bytes here and there, how will the memory
ever get marked for a free call? 

The OS does not know when the runtime is done with a piece of memory, no
matter how large or small. My point is that the runtime does not neccessarily
even bother to do the space reclamation. So if I write a program that
runs under UNIX in Ada that requires heap space sometimes, I want to be able
to reclaim that heap space. Either explicitly or by knowing that the Ada
runtime will do it for me.

When I am using the Ada "new" keyword, I know full well that I'm creating
potential for garbage. I can control when I call it. I can write my own
memory manager around it. I don't get that control when I need a construct
like

	declare
		c_string : constant string := "foo" & ascii.nul;
	begin
		<stuff>
	end;

If I can't even trust Ada to reclaim the 4 or so bytes used by foo, what
can I trust Ada to do????



Allan Doyle                                        adoyle@bbn.com
BBN Systems and Technologies Corporation           (617) 873-3398
70 Fawcett Street,   Cambridge, MA 02138

adoyle@bbn.com (Allan Doyle) (06/12/90)

In article <EMERY.90Jun11135320@scorpio.linus.mitre.org>
 emery@linus.mitre.org (David Emery) writes:
>Allan Doyle writes
>>If I take the worst case assumption that c_string
>>is being allocated from the heap and add it to the worst case assumption
>>that my Ada run-time will not be doing garbage collection (perfectly legal,
>>I understand), then how many of these little beasties can I convert from
>>Ada to C before I run out of memory?
>
>It's not clear to me that you can guarantee with a C compiler that
>there is not some compiler-generated heap, either.  Does the C
>LANGUAGE guarantee non-heap allocation for a local variable???    Or
>does "everyone knows that's how it's done?"
>
> 				dave emery
>				emery@aries.mitre.org


Touche' - I just looked in K&R and found no statement concerning
automatic variables and the memory they occupy. It only states that
the variables "disappear" from scope.

By the way, the example I gave in the previous post:

	declare
		c_string : constant string := "foo" & ascii.nul;

would take up heap in C as well. I really meant 

	proc(foo: string) is
		c_string : constant string := foo & ascii.nul;
	...

which everybody "knows" will go on the stack in C...(sigh)





Allan Doyle                                        adoyle@bbn.com
BBN Systems and Technologies Corporation           (617) 873-3398
70 Fawcett Street,   Cambridge, MA 02138

kassover@minerva.crd.ge.com (David Kassover) (06/12/90)

In article <57288@bbn.BBN.COM> adoyle@vax.bbn.com (Allan Doyle) writes:
...
>When I am using the Ada "new" keyword, I know full well that I'm creating
>potential for garbage. I can control when I call it. I can write my own
>memory manager around it. I don't get that control when I need a construct
>like
>
>	declare
>		c_string : constant string := "foo" & ascii.nul;
>	begin
>		<stuff>
>	end;

I'll ask for some clarification from the language lawyers (and
others who have experience with specific Ada environments other
than VAX Ada)

but I don't think garbage collection in this instance was ever an
issue.

That is, I have used similar constructs (and rather large ones,
too: potential to store 90000 g_floats) in VAX Ada.  It, so far,
has appeared that when the declared variables go out of scope,
the memory is returned to "the system".

On the other hand, stuff that is created with NEW is often
intended *not* to go out of scope, so unchecked_deallocation is
needed to get rid of it (presumably, *after* the program has
verified that "no one else" still points to it)

And furthermore, I remember having a discussion about this about
a year ago, before I implemented the monster data structure (we
use it to pass potentially large arrays to and from a FORTRAN
program, especially in instances where it is hard for the Ada
code to determine the dimensions of the array.  I was assured
that exhaustion of "heap space" would not be a problem, given
that there was enough heap available for any one scope.  But
again, that may be a "feature" of VAX Ada's way of dealing with it.

--
David Kassover             "Proper technique helps protect you against
kassover@ra.crd.ge.com	    sharp weapons and dull judges."
kassover@crd.ge.com			F. Collins

murphy@mips.COM (Mike Murphy) (06/12/90)

In article <57288@bbn.BBN.COM> adoyle@vax.bbn.com (Allan Doyle) writes:
>When I am using the Ada "new" keyword, I know full well that I'm creating
>potential for garbage. I can control when I call it. I can write my own
>memory manager around it. I don't get that control when I need a construct
>like
>
>	declare
>		c_string : constant string := "foo" & ascii.nul;
>	begin
>		<stuff>
>	end;
>
>If I can't even trust Ada to reclaim the 4 or so bytes used by foo, what
>can I trust Ada to do????

Most Ada compilers will reclaim the space used by an object when it 
exits its scope.  I say "most" because this is not required by the 
semantics of the language, but any good compiler will have this feature, 
as it is needed for large software systems, and it is not too difficult
to implement.  Basically, you emit some code at the end of any blocks 
or statements that use temporary space, to reclaim that space.
This might mean a "free", or it could be something even cheaper 
(e.g. some compilers will put these kind of block-structured temporaries 
on a mark-release stack).  You can code up a little example like the above
to test what your compiler does.  This is not considered to be "real"
garbage-collection, which no Ada compiler I know of does, which requires
searching through memory for dangling pointers.  Instead this is just
an important optimization for a common situation in Ada.  The situation
is more common in Ada than in languages like C because we can have
function calls that return dynamic-sized objects, such as strings,
and these need temporary space.  The reclaiming of the space is helped
by the block-structured nature of its scope (as opposed to the
full-blown garbage collection problem).

-- Mike Murphy
-- UUCP: sun!decwrl!mips!murphy or murphy@mips.com

defaria@hpclapd.HP.COM (Andy DeFaria) (06/12/90)

One of my concerns about coverting Ada Strings to C Strings  was the amount
of overhead involved in allocating heap space.  Dave  Emery stated that the
compiler should  not allocated any  heap space  for a  local variable -  it
should be placed in the stack:

declare
   C_STRING : constant STRING := STRING_PARAM & ASCII.NUL;
begin
   -- call C routine
end;

My understanding is that the compiler would allocate space on the stack for
this variable if the size of STRING_PARAM can be determined at compile time
not at run time.  Since STRING_PARAM is being passed into my routines it is
not  known, at compile time,  how big   the  STRING_PARAM will be (it could
theoretically (sp?) be INTEGER'LAST bytes long!!  Assuming a 32 bit integer
size  this could be gigantic  and with  some  architectures  stack space is
limited and heap space is more plentifull) so  the compiler might determine
that it is better to put this variable on the heap  instead.  This does not
mean  that the compiler   is  faulty.   This also  might   help  facilitate
procedure cleanup.

Also, no one has  address  my other  concern about  how to represent  these
strings in structures such as:

struct data_record {
   char *path;
   int  path_length;
   char *name;
   int  name_length;
}

emery@linus.mitre.org (David Emery) (06/12/90)

defaria@hpclapd.hp.com writes
>My understanding is that the compiler would allocate space on the stack for
>this variable if the size of STRING_PARAM can be determined at compile time
>not at run time.  Since STRING_PARAM is being passed into my routines it is
>not  known, at compile time,  how big   the  STRING_PARAM will be (it could
>theoretically (sp?) be INTEGER'LAST bytes long!!  

There is no requirement (in Ada or any other language) that only
objects who size is static at compile-time be allocated off the stack.
The requirement, instead, is that the size of the object be STATIC at
runtime.  The size of STRING_PARAM is known when the stack frame for
the block is built.  For instance, if STRING_PARAM is 7 characters
long, then a string of length 7 is allocated off the stack.  Almost
any block-structured language can do this (including C and Pascal).
To insure that the object being declared (C_STRING) remains static, it
is declared as a constant.  

Ada provides substantial support for types and subtypes whose size is
not known until runtime.  There are some things you can't do with
them, but in general storage allocation is not a problem.  That is
because all objects in Ada are required to be constrained, either with
a fixed size, or with a maximum size.  In the case of variant records
with defaults, the maximum size is determinable based on the range of
values of the discriminant.  

There is always the problem with overflowing either stack or heap.  If
you have a string where 'LENGTH = INTEGER'LAST, I doubt that it will fit
anywhere in main memory, and you'll get STORAGE_ERROR long before you
try to append a ASCII.NUL to it and pass it to C.

				dave emery
				emery@aries.mitre.org

murphy@mips.COM (Mike Murphy) (06/13/90)

In article <920026@hpclapd.HP.COM> defaria@hpclapd.HP.COM (Andy DeFaria) writes:
>Also, no one has  address  my other  concern about  how to represent  these
>strings in structures such as:
>
>struct data_record {
>   char *path;
>   int  path_length;
>   char *name;
>   int  name_length;
>}

It seems to me that the simple solution is to just define a c_string
type which is an access to a constrained string, and then some routines
to convert strings to c_strings and vice-versa.  Then you define your
records to match the C structures:
	type data_record is record
		path 		: c_string;
		path_length 	: integer;
		name 		: c_string;
		name_length 	: integer;
	end record;
The cost of converting strings to c_strings is probably not as bad as you 
imagine, depending on your application (e.g. chances are you will only
convert string to c_string once, then do many calls to C routines with
the c_string structures).

BTW, Verdix uses such an approach in their UNIX interfaces (there is a
c_strings package in the standard library).

-- Mike Murphy
-- UUCP: sun!decwrl!mips!murphy or murphy@mips.com

stt@inmet.inmet.com (06/13/90)

Re: C Strings in Ada
Please listen to Mike Murphy.  He knows of what he speaks!

In particular, many compilers do *not* use the heap for
objects of dynamic size.  Instead, they use a mark/release discipline
on a stack, either the primary stack or a secondary stack.

Secondly, despite David Emory's appropriate warning, *most* Ada
compilers will treat access-to-*constrained*-string as a simple
address pointing directly at the characters.  You will still
have to worry about the null terminator, since Ada is presuming
the pointed-to string is quite long.

S. Tucker Taft
Intermetrics, Inc.
Cambridge, MA  02138

falis@ajpo.sei.cmu.edu (Edward Falis) (06/14/90)

you're right: it depends on the compiler and rte implementation. In fairness,
though, several of the more prominent Ada vendors do perform automatic
reclamation (though not garbage collection).  The typical scheme is
to deallocate all objects of the collection type when the access type
goes out of scope.  For compiler-generated temporaries, reclamation 
will occur when the scope creating the tempo is exited, or when the
tempo is no longer required, depending on the thoroughness of the
implementation.  - Ed Falis, Alsys

jb@rti.rti.org (Jeff Bartlett) (06/15/90)

In article <920025@hpclapd.HP.COM>, defaria@hpclapd.HP.COM (Andy DeFaria) writes:
> >/ hpclapd:comp.lang.ada / emery@D74SUN.MITRE.ORG (David Emery) / 10:21 am  Jun  6, 1990 /
> >>What is the best way to represent C_Strings in Ada?  
> >
> >	procedure to_c (str : string) 
> >	is
> >	  procedure c_routine (addr : system.address);
> >          pragma interface (C, c_routine);	-- (void) c_routine(s)
> >						-- char * s;
> >	  c_string : constant string := str & Ascii.NUL;
> >	begin
> >	  c_routine(c_string(c_string'first)'address);
> >	end to_c;

> ... my critical concerns: What about  the overhead incurred by
> having  to call the  heap manager to allocate a  new string just  to append
> ASCII.NUL so  that the C routine I'm   calling  will be  able to handle  it
> properly. ...

A good compiler should grow the stack by (str'length + 1 + string_descriptor)
then copy 'str' and the NUL into the area.  It would also fill in the
new string_descriptor for 'c_string' from the information in 'str's descriptor
and the additional byte.  (A smart compiler may not even allocate space for
the new descriptor, keeping the information in registers).

Constant strings are a good 'trick'. Such as:

    ...
	str : string(1..10);
	N   : natural;
    begin
	...

	-- a subroutineless call like above
	declare
           s : constant string := str & ascii.nul;
        begin
           c_routine( s(s'first)'address );
        end;

	-- remove the leading space supplied by 'image.
	declare
	   s : constant string := integer'image(N);
        begin
           text_io.put_line("Activity on PE" & s(s'first+1..s'last) );
        end;

	-- figuring out how much of a buffer to overwrite
        declare
           s : constant string := function_that_returns_a_string(j);
        begin
           line(line'first..line'first+s'length-1) := s;  -- result used twice
        end;

> Another, problem that you haven't addressed  is that  of the representation
> of a string in a record.  Should it be  and  Ada access to string or should
> it be the string itself.  Or should it be a C string (char *)?

It depends on what operations need to be performed on it.  I'll leave this for
someone else.  Don't forget that you can:

	type s_ptr is access string;
        p : s_ptr;
    begin
        ...
	-- this will call the function, allocate space for the result, and
        -- initialize the area with the result.
        p := new string'( ada_function_that_returns_a_string );
        ...
	text_io.put_line("answer was '" & p.all & "'");


If you are paranoid about the Ada run-time heap manager forgetting to free
the space pointed to by 'p', you can instantate a new UNCHECKED_DEALLOCATION.

Hope this helps,

Jeff Bartlett
Research Engineer, Center for Digital Systems Research
Research Triangle Institute, RTP NC.
jb@rti.rti.org     mcnc!rti!jb     rti!jb@mcnc.org     (919)-541-6945

PS: The code above was only compiled with a chemical computer. ;-)