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:
fun myFunction(result : UInt8**) : Bool
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)’
I think that what you need is something as follows:
result = uninitialized Pointer(UInt8)
There is a a String.new that converts from
Depending on the C library there might need to be a call to release the buffer after it’s copied to the Crystal String.
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?
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.
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?
In fact my function had another argument, so I made another test with only one argument and your suggestion is working fine. Thank you
I tried with the ‘out’ then and it is also working, but I also need to do the String.new() .
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)
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.
I have two follow-ups if you don’t mind:
- Is it possible to determine just by function signature whether it allocates or doesn’t?
- Will Crystal’s GC take into account the memory that was allocated inside external C libs?
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])
Note that I did not tried this yet so I don’t know if it works.
You could pass a buffer of
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