Get the sum value of a bit_set?

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? :thinking:

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. :thinking:
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