Interfaces.C.Strings and memory leak

I’m interfacing the C API of a C++ library and I’d like to avoid (obvious?) memory leaks / bugs.

To pass a char* to a C procedure:

  1. I first convert my Ada String to a Interfaces.C.Strings.chars_ptr with Interfaces.C.Strings.New_String.
  2. Then I call the C procedure with the resulting chars_ptr.

But should I:

  • Free the temporary chars_ptr just after the call?
  • Or do nothing?

In the first case, I don’t have a memory leak, but I may end with a dangling reference in the C code.
In the second case, I have a probable memory leak (as I understand).

Same question when a C function / procedure returns a char*. If I convert the chars_ptr to a String with Interfaces.C.Strings.Value:

I suppose similar questions arise in C code when interfacing an Ada library :->

Thanks

Replying to myself.

Valgrind tells me I should free the temporary chars_ptr as it complains for memory leaks if I don’t.

For the second point, I’m still not sure how to free the chars_ptr returned by C functions. Should I bind the C free function and then call it on the chars_ptr?

If it’s a char * parameter, don’t use chars_ptr, use char_array and To_C, it doesn’t allocate memory.

1 Like

See ARM B.3(70). As Lucretia has pointed out, for

void f(char* s);

you can use

procedure F (S : in out char_array);

and the compiler will deal with it for you. The only problem is when you have

char* f (...);

Then it depends on what the C is giving you. Typically the C library will tell you if the ptr needs to be freed or not.

Note that C often uses char* to mean an array of bytes, not characters. They’re the same in C, but not in Ada, so your Ada should not be using char_array or chars_ptr, but something numeric for those cases. (I once wrote down the 12 Ada equivalences to void f (char* s);. That was before the aliased parameter mode was introduced, so now I guess there are 16.)

Thanks, it’s clearer for me now.

I used to use chars_ptr for null terminated strings and char_array for others, but I was wrong.

As a side but related question, I have the following (simplified here):

procedure Wrapper (Buffer : Interfaces.C.char_array;
                   Length : Interfaces.C.size_t) with
  Convention => C;

and the types:

type Byte is range 0 .. 255;
for Byte'Size use 8;

type Message is array (Positive range <>) of Byte with
  Pack, Convention => C;

Wrapper is an Ada procedure called from C by a callback. I’m using char_array for the moment and convert the Buffer to a Message with a byte by byte copy (ugly but it works).

Is there a way to bypass that extra step?

It works, if I use:

type Message_1024 is new Message (1 .. 1024);
procedure Wrapper (Buffer : Message_1024;
                   Length : Interfaces.C.size_t) with
  Convention => C;

But, I don’t know the length in advance, and not sure it’ll always be ≤ 1024.

You might be able to do memory-overlaying.
Now, taking a step back from C, let’s approach the problem of Pascal-style Strings: the first byte indicating the length, the text.

Subtype Byte_Positive is Byte range Byte'Succ(Byte'First)..Byte'Last;
Type Byte_Vector(Byte_Positive range <>) of Character;
Type Byte_String( Length: Byte ) is record
   Data : Byte_Vector(1..Length);
End record With Size => Character'Size*256;

This is nice and represents what we want quite well, but there’s a terrible problem here: we can’t resize a value! so…

Package Example is
   Type Pascal_String is private;
   Function "+"(Input : String) return Pascal_String
      with Pre => Input'Length in Byte'Range;
   Function "+"(Input : Pascal_String) return String;
   Procedure Replace( Item : in out Pascal_String; New_Value : String )
      with Pre => New_Value'Length in Byte'Range;
   -- etc…
Private
   Type Pascal_String is record
      Length : Byte;
      Data   : String(1..255);
   end record with Size => 256*Character'Size;
End Example;

Package Body Example is
   Function "+"(Input : String) return Pascal_String is
   ( Length => Byte(Input'Length),
     Data   => Input & (Input'Length+1..255 => ASCII.NUL)
   );

   Function "+"(Input : Pascal_String) return String is
   ( Input.Data(1..Natural(Input.Length)) );
---…
End Example;

Now, depending on your application, you could make one form the “public” form and one “private” – Say making the discriminated version public with the non-discriminated version private; then when you need to alter the discriminated version your calls would do something like:

Procedure Reset( Object : in out Byte_String ) is
  Item : Pascal_String with Import, Address => Object'Address;
Begin
  Item.Length:= 0;
  Item.Data:= (others => ASCII.NUL);
End;

Which is overlaying the two views of the 256-Byte that the two types represent.

Do you actually need to know in advance?
Text : String renames Ada.Text_IO.Get_Line; creates a perfectly-sized buffer from unknown-at-compile-time user-data.