How to use C strings from crystal? And varargs

Let’s say I want to create a wrapper for printf( char *fmt, ... ). How do I convert map char * to a crystal type? The manual doesn’t mention strings under the C bindings topic as far as I see.

And does crystal support varargs, either native ones to use within crystal code or C ones to use with bindings?

1 Like

That’s just a Pointer(LibC::Char)

And to use it from Crystal, if you know the size:

String.new(Slice.new(fmt, size))
1 Like

Thanks for your answer!

In the crystal source code I see LibC::Char as being defined as an alias of UInt8, so leading to the same I had before:

lib C
    fun printf( format : Pointer(UInt8) )
end

But C.printf "Hi" results in a fail to compile:` Error: argument ‘format’ of ‘C#printf’ must be Pointer(UInt8), not String.

How can I convert a string into an array of UInt8s?

Edit: I found how to do varargs in Crystal but how to interact with C functions that require varargs?

See the source code for Crystal’s standard library.

Then

s = "I am a string."
LibC.printf(s)

To use string pointers with Crystal

@[Link("curl")]
lib Curl
  # Any of them will work.
  # fun curl_version : LibC::Char*
  # fun curl_version : UInt8*
  # fun curl_version : Pointer(LibC::Char)
  fun curl_version : Pointer(UInt8)
end

puts String.new(Curl.curl_version)
1 Like

I found a nice example in to_unsafe - Crystal

If a type defines a to_unsafe method, when passing it to C the value returned by this method will be passed.
For example, the String class implements to_unsafe to return UInt8*:

lib C
  fun printf(format : UInt8*, ...) : Int32
end

a = 1
b = 2
C.printf "%d + %d = %d\n", a, b, a + b
C.printf "Static Text\n"
$ crystal --version
Crystal 1.16.2 (2025-04-29)

LLVM: 20.1.3
Default target: aarch64-apple-darwin24.4.0

$ crystal run libc_example.cr
1 + 2 = 3
Static Text
1 Like

I tried to use str.as(UInt8*) to get the string as an array of UInt8 but while this is fine with the original prelude, it triggers a bug when I try to do this with my own minimal prelude.

crystal build --prelude ./prelude.cr obj.cr

Module validation failed: Function return type does not match operand type of return inst!
ret i32 %0, !dbg !28
i64 (Exception)
from crystal in ‘??’
from crystal in ‘??’
from crystal in ‘??’
from crystal in ‘??’
from crystal in ‘??’
from crystal in ‘__crystal_main’
from crystal in ‘??’
from crystal in ‘main’
from /usr/lib/libc.so.6 in ‘??’
from /usr/lib/libc.so.6 in ‘__libc_start_main’
from crystal in ‘_start’
from ???
Error: you’ve found a bug in the Crystal compiler. Please open an issue, including source code that will allow us to reproduce the bug: GitHub · Where software is built

I guess I need to use a (different) type declaration somewhere but it isn’t obvious to me where.

to_unsafe needs to be written first without the prelude and when I try to write a wrapper it seems to just return the same string as String.

1 Like

Got it. I’ve bookmarked an old projects — lilith and crystal-kernel — in case I decide to write my own personal prelude someday.
For further reading or inspiration, you might also find the Crystal source for String interesting: string.cr.

IIRC: to_unsafe

Confirmed that the following code works with crystal build --prelude empty

@[Link("c")]
lib LibC
  fun printf(format : UInt8*, ...) : Int32
end

# Open class or Monkey Patch for Ruby / Crystal
class String
  def to_unsafe : UInt8*
    pointerof(@c)
  end
end

str = "str"
LibC.printf(str)

I believe the language reference on fun calls should answer your questions: fun - Crystal