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:
I first convert my Ada String to a Interfaces.C.Strings.chars_ptr with Interfaces.C.Strings.New_String.
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:
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?
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.)
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.