Hi forum,
I’m having trouble to figure out how to go from the bit_set
to the c.ulong
needed for the C API function I need to call.
Here is my naive attempt in a self-contained file:
package test_bit_set
import "core:c"
import "core:fmt"
PortFlag :: enum c.int {
// Docs: https://jackaudio.org/api/types_8h.html#acbcada380e9dfdd5bff1296e7156f478
IsInput = 1,
IsOutput = 2,
IsPhysical = 4,
CanMonitor = 8,
IsTerminal = 16,
}
PortFlags :: distinct bit_set[PortFlag;c.int]
main :: proc() {
input_flags: PortFlags = {PortFlag.IsInput}
output_flags: PortFlags = {PortFlag.IsOutput, PortFlag.IsPhysical}
output_flags_ulong: c.ulong = c.ulong(output_flags) // <- how to do this??
fmt.println("Output flags ulong", output_flags_ulong)
}
If I try to build/run this with odin run port_flags.odin -file
I get the error:
port_flags.odin(21:40) Error: Cannot cast 'output_flags' as 'u64' from 'PortFlags'
output_flags_ulong: c.ulong = c.ulong(output_flags)
^~~~~~~~~~~^
How can I do this?
Thanks in advance!
Oh!! I can just loop over the bit_set
!
main :: proc() {
input_flags: PortFlags = {PortFlag.IsInput}
output_flags: PortFlags = {PortFlag.IsOutput, PortFlag.IsPhysical}
output_flags_ulong: c.ulong
for c in output_flags {
fmt.println("element is set", c)
output_flags_ulong += u64(c)
}
fmt.println("output_flags_ulong", output_flags_ulong)
}
That was much simpler than what I thought!
Anyway, let me know if there is a better way of doing this =)
transmute
is what you want, not a cast
. Transmutation is a bit-for-bit conversion, whereas cast
will attempt to do type conversion if possible.
You transmute the bit_set
to its underlying type (c.int
in this case), then cast that to c.ulong
.
2 Likes
Hello @Feoramund – thanks for the answer!
That sounds more elegant indeed.
However, when I try the transmute
approach I get a weird value 20
instead of the 6
(2 + 4) that I was expecting.
Full program:
package test_bit_set
import "core:c"
import "core:fmt"
PortFlag :: enum c.int {
// Docs: https://jackaudio.org/api/types_8h.html#acbcada380e9dfdd5bff1296e7156f478
IsInput = 1,
IsOutput = 2,
IsPhysical = 4,
CanMonitor = 8,
IsTerminal = 16,
}
PortFlags :: distinct bit_set[PortFlag;c.int]
main :: proc() {
input_flags: PortFlags = {PortFlag.IsInput}
output_flags: PortFlags = {PortFlag.IsOutput, PortFlag.IsPhysical}
output_flags_ulong: c.ulong
for c in output_flags {
fmt.println("element is set", c)
output_flags_ulong += u64(c)
}
fmt.println("output_flags_ulong with for-loop", output_flags_ulong)
output_flags_ulong = c.ulong(transmute(c.int)output_flags)
fmt.println("output_flags_ulong with transmute", output_flags_ulong)
}
The above prints the correct value for the for-loop, but a funny value for one calculated with transmute:
element is set IsOutput
element is set IsPhysical
output_flags_ulong with for-loop 6
output_flags_ulong with transmute 20
Any idea on what am I missing? 
Right, so in this instance, you wouldn’t set the values of PortFlag
, as a bit_set
uses the value of the enum as a bit index rather than a value to be OR’d. If you remove the = [powers of two]
assignments from the enum, the transmute
results in 6
as expected.
2 Likes
Ohhh, that makes sense, thank you!
Right, so my enum type won’t match the values as in the C API. 
That might be okay, I just need to double check if it makes sense for my case.
Thank you for your help, have a good day!
No, that enum type won’t match the C API values.
You can either use the bit_set
as an interface where you transmute to and from that type when inter-operating with C, or you can have both a bit_set
that refers to one enum where the values are all contiguous integers that refer to bit indexes, then you have a separate enum for the exact value that can be used with individual bit operations.
2 Likes
Right!
From a quick check, it seems that the flags are never used alone, so I think I’ll keep the bit_set
without them.
If someday I truly need them, I guess I can just do 1 << PortFlag.IsOutput
or something.
Thanks again!
1 Like