Using pointer of pointers in C


#1

Hello,

Can you tell me how to call a C function from Crystal which returns a string in argument?
bool myFunction(char ** result);

I tried the following:
lib MyCLib
fun myFunction(result : UInt8**) : Bool

MyCLib.myFunction(pointerof(result))
and this gives anerror: must be Pointer(Pointer(UInt8)), not Pointer(String)
I tried also with MyCLib.myFunction(out result) which gives an undefined reference, or pointerof(pointerof(result) which gives ‘can’t take address of pointerof(result)’


C function with pointer of pointer
#2

I think that what you need is something as follows:

def my_func
  result = uninitialized Pointer(UInt8)
  if MyCLib.myFunction(pointerof(result))
    String.new(result)
  end
end

There is a a String.new that converts from Pointer(UInt8).

Depending on the C library there might need to be a call to release the buffer after it’s copied to the Crystal String.


#3

Thank you for your answer. I tried your suggestion but I am getting a ‘undefined reference to myFunction’ error then. Should I change the signature of myFunction?


#4

If you are getting something like:

Undefined symbols for architecture x86_64:
  "_myFunction", referenced from:
      _*my_func:(String | Nil) in _main.o

Then it’s about the actual myFunction that you are trying to link to.

Note that besides def my_func I suggest you still need the lib MyLib from your post.


#5

Just a quick question on a related topic: is it safe to do this? There is no mention of the buffer size anywhere I can see, what stops the function from overwriting the memory?


#6

In fact my function had another argument, so I made another test with only one argument and your suggestion is working fine. Thank you :slight_smile:
I tried with the ‘out’ then and it is also working, but I also need to do the String.new() .


#7

Also check the docs for out arguments used when calling C functions: https://crystal-lang.org/reference/syntax_and_semantics/c_bindings/out.html

I think it can help you here (instead of using uninitialized, it avoids that boilerplate)


#8

There is another String.new overload that is safer.

Note that in the snippet I sent, the C function is responsible for allocating the char* and crystal will duplicate it’s value. So as long as there is a \0 somewhere in the accessible memory it is safe.


#9

I have two follow-ups if you don’t mind:

  1. Is it possible to determine just by function signature whether it allocates or doesn’t?
  2. Will Crystal’s GC take into account the memory that was allocated inside external C libs?

#10
  1. Is it possible to determine just by function signature whether it allocates or doesn’t?

No

  1. Will Crystal’s GC take into account the memory that was allocated inside external C libs?

No


#11

You concerns are valid stronny, I was just making a test. The recommended approach would be to allocate the String in Crystal and give the pointer to the C library with the allocated length, or if we do not know in advance the maximum length of the string, add another function in the C library that returns the length of the String to allocate on Crystal side, giving something like that:
fun GetLenthNeeded() : Int32

len = MyLib.GetLenthNeeded()
result = String.new(UInt8[0,len])
MyLib.MyFunction(result, len)

Note that I did not tried this yet so I don’t know if it works.


#12

You could pass a buffer of Slice(UInt8) or Pointer(UInt8) or length to the C function for it to write the content up to certain length. That would make the buffer managed by the GC. But being safe would depend on the C function.

You will still need to create a Crystal String from the buffer. Since the String class does not match with a C struct.