I’m trying to write some Crystal bindings for the libical C-library.
I found Callbacks - Crystal and tried to use it to adapt the icalparser_get_line function.
Here’s the relevant part of libical.cr:
@[Link("ical")]
lib LibIcal
# ...
fun get_line = "icalparser_get_line"(
parser : LibIcal::IcalParser*,
line_gen_func : (LibC::Char*, LibC::SizeT, Void* -> LibC::Char*),
data : Void*
) : LibC::Char*
end
I then tried to use the function like so:
require "../../src/app.cr"
module Ical
# The callback for the user doesn't have a Void*
@@box = Pointer(Void).null
def self.line_gen_callback(parser : LibIcal::IcalParser, &callback : ((LibC::Char*, LibC::SizeT, Void*) -> LibC::Char*))
# Since Proc is a {Void*, Void*}, we can't turn that into a Void*, so we
# "box" it: we allocate memory and store the Proc there
boxed_data = Box.box(callback)
# We must save this in Crystal-land so the GC doesn't collect it (*)
@@box = boxed_data
# We pass a callback that doesn't form a closure, and pass the boxed_data as
# the callback data
LibIcal.get_line(pointerof(parser), -> (s : LibC::Char*, size : LibC::SizeT, data : Void*) {
p "here"
p s
p size
p data
# Now we turn data back into the Proc, using Box.unbox
data_as_callback = Box(typeof(callback)).unbox(data)
# And finally invoke the user's callback
data_as_callback.call(s, size, data)
}, boxed_data)
end
end
parser = LibIcal.new_parser
File.open("basic.ics") do |file|
LibIcal.set_gen_data(pointerof(parser), pointerof(file))
line = -1
until line == 0
Ical.line_gen_callback(parser) do |str, size, d|
p "there"
line_string = file.gets
p line_string
if line_string.nil?
Pointer(LibC::Char).null
else
Intrinsics.memcpy(str, line_string, line_string.size, false)
str
end
end
end
end
I’m getting the following error:
Invalid memory access (signal 11) at address 0x0
[0x102cd15c0] *Exception::CallStack::print_backtrace:Nil +104 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
[0x102cb7b48] ~procProc(Int32, Pointer(LibC::SiginfoT), Pointer(Void), Nil)@/opt/homebrew/Cellar/crystal/1.11.2/share/crystal/src/crystal/system/unix/signal.cr:131 +320 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
[0x188c83584] _sigtramp +56 in /usr/lib/system/libsystem_platform.dylib
[0x102cbcfd8] ~procProc(Pointer(UInt8), UInt64, Pointer(Void), Pointer(UInt8))@tmp/console/1711652312560_console.cr:17 +152 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
[0x10355e728] icalparser_get_line +216 in /opt/homebrew/Cellar/libical/3.0.17_1/lib/libical.3.0.17.dylib
[0x102e7aea8] *Ical::line_gen_callback<struct.LibIcal::IcalParser, &Proc(Pointer(UInt8), UInt64, Pointer(Void), Pointer(UInt8))>:Pointer(UInt8) +2932 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
[0x102c9e51c] __crystal_main +8888 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
[0x102e7c23c] *Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil +12 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
[0x102e7c1a4] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +60 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
[0x102ca7a8c] main +12 in /Users/nerdinand/.cache/crystal/crystal-run-eval.tmp
"here"
Pointer(UInt8)@0x16d166490
72057594037927936
Pointer(Void)@0x1031edf60
Any help with this is highly appreciated! I’m pretty new to Crystal, coming from Ruby and Python.