#Polymorphism over const-pointer-or-value

1 messages · Page 1 of 1 (latest)

sour badge
#

Is there a convention or idiomatic way to be sort of "ergonomirally polymorphic" or agnostic about something being a pointer-to-a-const-T or a T:

pub fn product(values: []const PtrToIntegerOrInteger) Integer {
   ...
}

I know the default way of writing arguments, like
fn zzz(x: SomeStruct) void { } you're saying you're agnostic to whether the compiler decides to pass you a reference pointer or copy-by-value-- (do I understand that correctly?)

I'm looking for something like that but for more complex types, like the above. Sometimes I have slices of pointers, sometimes I have slices of direct structs, and for functions like the above where I'm not going to do anything but read out the values I don't really care about the difference.

I know I can take anytype and do comptime checks on it, but it feels like it would lead to duplication of code, as I'd have two near-identical functions, one for each such case? only some p.mul(el) and p.mul(&el) difference, and copy-pasted code leads to errors as I change one and forget to change the other, etc. (this might look trivial, above, but it uses a better algorithm than the naive one to multiply balanced values..) Any way to comptime this without duplicating code? Also have many such functions, sums, polymul, convolutions, etc.

And/or wondering if I'm going about it all wrong and the idiomatic way to handle this is way different?

(Of course I don't want anything runtime, like a tagged union.. I essentially want the compiler to compile two versions of the function, one thinking that T is a pointer, one thinking T is a value.)

reef ore
#

anytype is the only real option if it needs to be polymorphic

#

i would question if it really needs to be though. isn't a single function that takes *const T enough? you can take a temporary reference to the value to call the function if you don't already have it in pointer form

plucky apex
#

the fn zzz(x: SomeStruct) isn't really about you being agnostic about pointer vs value, it's for the compiler, for the user it's just a const x=... and nothing else

#

yes, it's done that way to allow optimization, but only in the specific case of function calls and it doesn't apply anywhere else

trim fog
#

It seems odd that you need the same function to take either a value or a mutable pointer. 🤔
Normally, you want one or the other; if you're mutating the caller's value, then you need a pointer.
If not, then you take either a const-pointer or a value.

How come you want to be generic here?

jolly swift
#

i have run into this problem when writing a member function for getting to another struct through @fieldParentPtr where you want to propagate the constness if present

#

or when you want to do pointer arithmetic on an existing pointer in general

#

ok i just realized the question is about taking a const pointer or a value which is different

sour badge
# trim fog It seems odd that you need the same function to take either a value or a mutable...

hmm, not a mutable pointer. basically everything const all the way down. Just that if everything is const all the way down, I wanted to sort of be agnostic to whether things were references or direct copies.

This was/is related to computational number theory with GMP. GMP defines its numbers as struct __something[1] so you put them on the stack (normally) and then just pass a pointer around. In my case, why it might seem strange to want both slices of T and slices of *const T, is... there's numerous routines that A) permutes/filters/etc some other collection of numbers, in which case it's way less work to produce a slice of pointers into the original, (each number I make is an unavoidable alloc,) B) produce unique new numbers from various calculations, in which case it's easiest for them to alloc a []T and return that. thus I'm working with both []T and []*T

sour badge
# reef ore i would question if it really needs to be though. isn't a single function that t...

I don't know how necessary yet, I'm not that familiar with the "right way of doing things" in Zig, so see above. But like in the above message, I have []T and []*T from various sources, and kind of don't want to duplicate code for algorithms that "works on collections of T". But perhaps there is some easy way of avoiding []T for example, that I am missing.. if a function produces some list, let's say it finds prime numbers, it woud have to alloc []T internally, then alloc a new []*T giving pointers into it, but where does the []T go, it needs to be kept around somewhere etc. and I just feel it becomes messy. whereas copying []T around every time I do some permutation/filter pass on a list is very wasteful and involves a lot of allocs etc.

trim fog
sour badge
#

iterators might work for filters, tho permutations will not, presumably(?), like sorting (pointers) etc.

#

hm, maybe there could be some sort of comptime flag... tho that's gonna be very ugly

trim fog
sour badge
#

they are around that size, however their struct has an internal pointer, too. It's bad if two different copies of T has the same pointer

#

so I can't really copy them willy nilly, if the previous collection is still going to be used

#

the only option is to re-initialize new structs, however that involves alloc etc for each one

#

I think code duplication is really the option here, it seems to me, even though I hate it

trim fog
# sour badge I don't know how necessary yet, I'm not that familiar with the "right way of doi...

One improvement for permutations might be that you have a []T, and then you make a temporary []i32, which represents the indices into the []T.
You then use that to sort the Ts, and then mutate the []T to align with it.
This means you only need to copy around 4 bytes per item, and don't have to allocate new elements in order to sort it, beyond the indices slice itself -- which you were needing to do before anyway. 🤔

sour badge
#

hmm, yeah that's an idea

#

have some sort of concept of an ix slice

#

actually, quick q while I have you here, hehe. is there a way to return a simple anonymous tuple/struct, like fn divMod(...) .{ T, T } { ... }? Or I have to name a struct for it?

trim fog
#

It's planned for you to be able to return struct{ T, T }, but I'm not sure if that works yet.

#

If not, use a named struct for now.

sour badge
#

ah, cool

#

but thanks @trim fog you've been very helpful