Function pointer of static dispatch for some specific T

package main
import "core:fmt"

function_with_location :: proc() {}
function_without_location :: proc(i: $T) {}


main :: proc() {
	fmt.println("function_with_location: ", function_with_location)
	// Since function_without_location is created for each T we use it with it does not have a location by itself
	// But what if I want the instance of function_without_location for some specific T=int 
	// This must reside somewhere in the memory, but I don't see how to find this location
	fmt.println("function_without_location: ", function_without_location)

}
main.odin(13:45) Error: Cannot assign value 'function_without_location' of type 'proc($T)' to 'any' in a procedure argument
        ... rintln("function_without_location: ", function_without_location)

When commented out

function_with_location:  proc() @ 0x43B670

As you can see the generic function does not have an address, since an instance would be created for a specific T.
How would I get the address of the instance used when I say

i :int= 4
// The below call must go to some function in memory 
// The address of that function is what I want
function_without_location(i)

Does anyone know a way to get the address of a specific generic function instance?

So there is a way to get it, but I question why you want it. It’s extremely rare that you actually require it, and it’s usually a sign that you are doing something wrong.

To get it, use intrinsics.procedure_of(function_without_location(i)).

1 Like

Thank you for the reply, I was mostly exploring what I can do with Odin.
I tried to implement something like a generic Iterator and tried to apply a mapping to it.

This is how far I got in my questionable journey where I ran into the problem of not being able to get the pointer I need for map_iterate.
I might be a bit tainted by Haskell.

If something like this is doable without the jank, I’d be happy to be pointed into the right direction.

package main
import "core:fmt"
import "core:math"
import "core:testing"

Generic_Iterator :: struct($T, $S: typeid) {
	internal_state: S,
	iterfunc:       proc(_: ^S) -> (T, bool),
}
generic_iter :: proc(i: ^Generic_Iterator($T, $S)) -> (T, bool) {
	fmt.println("HO", i.iterfunc)
	return i.iterfunc(&i.internal_state)
}

Map_Iterator :: struct($T, $R, $S: typeid) {
	internal_iter: ^Generic_Iterator(T, S),
	f:             proc(_: T) -> R,
}

map_iterate :: proc(i: ^Map_Iterator($T, $R, $S)) -> (res: R, ok: bool) {
	temp: T
	temp, ok = generic_iter(i.internal_iter)
	res = i.f(temp)
}

Range_Iterator :: struct {
	start, end, step: int,
}

iterate :: proc(i: ^Range_Iterator) -> (res: int, ok: bool) {
	ok = true
	if i.step > 0 && i.start >= i.end {
		ok = false
	} else if i.step < 0 && i.start <= i.end {
		ok = false
	}
	res = i.start
	i.start += i.step
	return
}

generic_map :: proc(
	i: ^Generic_Iterator($T, $S),
	f: proc(_: T) -> $R,
) -> Generic_Iterator(R, Map_Iterator(T, R, S)) {

	mapiter := Map_Iterator(T, R, S){i, f}
	result: Generic_Iterator(R, Map_Iterator(T, R, S))
	result.internal_state = mapiter
	result.iterfunc = map_iterate
	return result
}

square :: proc(i: int) -> int {
	return i * i
}

main :: proc() {
	r := Range_Iterator{0, 10, 2}
	i := Generic_Iterator(int, Range_Iterator){r, iterate}
	fmt.println("main1", i.iterfunc)
	for i in generic_iter(&i) {
		fmt.println(i)
	}

	r = Range_Iterator{0, 10, 2}
	i = Generic_Iterator(int, Range_Iterator){r, iterate}
	i2 := generic_map(&i, square)
	i2.iterfunc = map_iterate
	fmt.println("main2", i2.iterfunc)
	for i in generic_iter(&i2) {
		fmt.println(i)
	}
}

Output:

main1 proc(^Range_Iterator) -> (int, bool) @ 0x41C670
HO proc(^Range_Iterator) -> (int, bool) @ 0x41C670
0
HO proc(^Range_Iterator) -> (int, bool) @ 0x41C670
2
HO proc(^Range_Iterator) -> (int, bool) @ 0x41C670
4
HO proc(^Range_Iterator) -> (int, bool) @ 0x41C670
6
HO proc(^Range_Iterator) -> (int, bool) @ 0x41C670
8
HO proc(^Range_Iterator) -> (int, bool) @ 0x41C670
main2 nil
HO nil
Segmentation fault (core dumped)

So the philosophy in Odin is to not try and do “generic” things in the first place unless you absolutely need to.

So don’t try to do this because it’s rarely, if ever, a good idea.

In the interest of philosophy, when you say “rarely, if ever, a good idea,” do you mean to constrain that claim to just Odin, or to apply it as a generic (ha!) statement about all of programming?

I’m sure since you designed Odin to avoid it, you have general opinions on it. Letting you know some people who appreciate simplicity in programming but aren’t deep into language design would probably appreciate an explanation to go alongside proscriptions of such conviction.

All of programming, not just Odin.

You are most likely solving specific problems and not general ones. Solve the specific problems first and then when you see a pattern that it should be generalized, only do it then.

2 Likes