A couple questions about C integration

yes, checked it myself.
Yes, Strings are supposed to be immutable so perhaps string literal was placed to readonly section.
For your case you can do:

@[Link(lib: "upcase", ldflags: "-L#{__DIR__}/../upcase-c/_build")]
lib LibUpcase
  fun upcase(s : UInt8*) : UInt32
end

def wrap_upcase(s : String) : String
  slice = Slice(UInt8).new(s.bytesize + 1) # allocate new (mutable) slice 
  s.to_slice.copy_to(slice) # copy String content to it
  slice[s.bytesize] = 0 # null-terminate it :)
  Lib1.upcase(slice)
  String.new(slice) # this will perform one more copy.
end

puts wrap_upcase("hello crystal")

note that it will perform copying of string twice - to the slice and from the slice to the returned string. If the performance is critical, you can use some black magic:

@[Link("#{__DIR__}/project1")]
lib Lib1
  fun upcase(s : UInt8*) : UInt32
end

def wrap_upcase(s : String) : String
  String.new(s.bytesize) do |buffer|
	buffer.copy_from(s.to_unsafe, s.bytesize)
	Lib1.upcase(buffer)
	{s.bytesize, s.size}
  end
end

puts wrap_upcase("hello crystal")

but that actually can break if strings aren’t null terminated - i was sure they are and this always worked, but well, i’ve never checked that it is documented somewhere.

1 Like