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 :->


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.