Simple type cast?

lib LibJack
type Nframes = UInt32
end

tm : LibJack::Nframes = 0_u32
Error: type must be LibJack::Nframes, not UInt32

tm : LibJack::Nframes = 0_u32.as(LibJack::Nframes)
Error: can’t cast UInt32 to LibJack::Nframes

tm : LibJack::Nframes = LibJack::Nframes.new(0_u32)
Error: undefined method ‘new’ for LibJack::Nframes.class

cannot cast UInt32 to UInt32 ???

you probably want to use alias instead of type. type is a bit more strict and has a bit more specialized use case.

Oh, you have started to make bindings to Jack? Cool! I’ve started a little with pipewire bindings, though I havn’t gotten that far yet…

the type def is “a bit more strict” == unusable ?
it’s hard to imagine why a cast of Int32 to Int32 might case any issue.

yes, here is the header for jackd audio, in case you need it, although very untestet yet:


@[Link("jack")]
lib LibJack
  # JACK API types
  alias Nframes = UInt32
  alias Client = Void*
  alias Port = Void*
  
  # Client management
  fun client_open = jack_client_open(name : LibC::Char*, options : Int32, status : Int32*) : Client
  fun client_close = jack_client_close(client : Client) : Int32
  fun client_name_size = jack_client_name_size : Int32
  
  # Port management
  fun port_register = jack_port_register(client : Client, name : LibC::Char*, type : LibC::Char*, flags : UInt64, buffer_size : UInt64) : Port
  fun port_unregister = jack_port_unregister(client : Client, port : Port) : Int32
  fun port_get_buffer = jack_port_get_buffer(port : Port, nframes : Nframes) : Void*
  
  # Transport
  fun transport_start = jack_transport_start(client : Client)
  fun transport_stop = jack_transport_stop(client : Client)
  fun transport_locate = jack_transport_locate(client : Client, frame : Nframes)
  
  # Callbacks
  alias ProcessCallback = (Nframes, Void*) -> Int32
  alias ShutdownCallback = (Void*) -> Void
  alias XRunCallback = (Void*) -> Void
  
  fun set_process_callback = jack_set_process_callback(client : Client, callback : ProcessCallback, arg : Void*) : Int32
  fun on_shutdown = jack_on_shutdown(client : Client, callback : ShutdownCallback, arg : Void*)
  fun set_xrun_callback = jack_set_xrun_callback(client : Client, callback : XRunCallback, arg : Void*) : Int32
  
  # Client activation
  fun activate = jack_activate(client : Client) : Int32
  fun deactivate = jack_deactivate(client : Client) : Int32
  
  # Time and tempo
  fun get_sample_rate = jack_get_sample_rate(client : Client) : UInt32
  fun get_buffer_size = jack_get_buffer_size(client : Client) : UInt32
  fun frame_time = jack_frame_time(client : Client) : Nframes
end

Its use case would make sense for your client_open function. If you make it type Client = Void* then the compiler would know that you can’t just pass any Void* to say client_close. It would only allow the return value of client_open since that’s the only function that returns a Client explicitly.

1 Like

yes, i like defining types, but if I cannot cast them (explicitly), in certain case it can become big trouble.

I agree that being able to define custom types wrapping Ints has it’s uses.

You can use unsafe_as thought it seems to have some rough edges.

Given,

lib L
  type T = Int32
end

Although the following complains

0.as(L::T)
          ^
Error: can't cast Int32 to L::T

The following works

0.unsafe_as(L::T)

The rough edges are:

v = 0.unsafe_as(L::T)
pp! typeof(v) # => Int32 (but is a L::T 🐛)
pp! v.class # => Int32 (but is a L::T 🐛)

And L::T#to_s will not work

v = 0.unsafe_as(L::T)
pp! v
There was a problem expanding macro 'macro_4743351584'

Code in src/int.cr:185:5

 185 | {% begin %}
       ^
Called macro defined in src/int.cr:185:5

 185 | {% begin %}

Which expanded to:

 > 2 | if other == 0
 > 3 |   raise DivisionByZeroError.new
 > 4 | elsif self < 0 && self == L::T::MIN && other == -1
                                 ^--------
Error: undefined constant L::T::MIN

And L::T are autocasted as Int32 if needed during def matching but then expanded using the original type.

def x(a : Int32)
  a
end

v = 0.unsafe_as(L::T)
x(v)

This works, but x(v) : L::T as shown if you do pp! x(v). Because you will get the previous error (not having a L::T::MIN).

1 Like

Depends on what you do with it. For example, I’ve found bugs when replacing alias with type as it no longer saw pointers to several different struct types defined as Void as equal. Generally I’d recommend wrapping the raw C bindings with more convenient things when possible instead of exposing them to end users. The C API often don’t make sense from a crystal perspective anyhow.

1 Like