I think I see your point now, thanks for explaining. You are right, I should avoid having this sense of "type security". The `Pointer[]` library is only doing syntatic sugar around the fundamentally unsafe casting of an `unsafe.Pointer`.
Having said that, unfortunately limiting it to a list of types in `CPointer` also doesn't solve the problem, because one could still choose the wrong one. On Sunday, April 16, 2023 at 9:44:52 AM UTC+2 Axel Wagner wrote: > The thing is, if it works with an arbitrary pointer type, it also works > with `*[1<<30]byte`, giving unsafe memory access to essentially the entire > memory space. Without any additional safeguards. You say it would "feel > cleaner without the additional lines of unsafe.Pointer floating around", > but that feeling is treacherous. Returning an unsafe.Pointer is honest and > clear, because it really *is* an unsafe operation. And adding generics to > the mix just gives off the false impression that you have a type-checked > operation, when you don't. > > I really think the right solution here is to provide wrappers that just > provide type-safe access to the underlying C calls. But you do you, of > course. > > On Sun, Apr 16, 2023 at 7:59 AM Jan <pfe...@gmail.com> wrote: > >> Thanks for the suggestions Alex. Interesting that enumerating the >> pointers in a constraint would work ... but it makes sense. >> >> I think I disagree with the statement about not wanting to allow any >> pointer type. From my project perspective there will be indeed a limited >> number of those (~10). But from the `PointerOrError` library (my StatusOr >> library) perspective, I think it shouldn't know which pointers it's being >> used for (or need to include every ".h" file I have). Ideally it would >> support arbitrary pointer type that a client of the library might want to >> use. >> >> Again ... not a big issue I can simply use the UnsafePointerOrError >> version, and cast to the desired pointer at the next line. It's just would >> feel clearner with one fewer line with a floating unsafe pointer around :) >> >> cheers >> >> On Saturday, April 15, 2023 at 10:54:47 PM UTC+2 Axel Wagner wrote: >> >>> I guess thinking about some more, I don't really understand the point of >>> what you're doing. It seems you *can* actually get the static guarantee you >>> want - you just have to spell the call >>> `(*C.mystruct)(UnsafePointerOrError(s))` instead of >>> `PointerOrError[C.mystruct](s)`. If you *could* restrict the type parameter >>> to be "any pointer", the two would actually be entirely equivalent, giving >>> you exactly the same guarantees. >>> >>> I think in reality you *don't* want to allow "any pointer type". I think >>> in reality you want the type to be dependent on the C++ function you are >>> calling - which returns the Status. I don't think you can use templated C++ >>> types, otherwise you would probably want to do something like >>> func Call[T any](f func(…) C.StatusOr<T>) (T, error) >>> >>> In the absence of that, you might try listing the types which are valid: >>> type CPointer interface{ >>> *C.MyStruct | *C.MyOtherStruct | *C.YourStruct >>> } >>> func PointerOrError[T CPointer](s C.StatusOr) (T, error) { … } >>> >>> >>> >>> On Sat, Apr 15, 2023 at 10:24 PM Axel Wagner <axel.wa...@googlemail.com> >>> wrote: >>> >>>> You should be able to instantiate the function using a Pointer. That >>>> is, you can write >>>> >>>> func PointerOrError[T any](s C.StatusOr) (t T, err error) { >>>> var ptr unsafe.Pointer >>>> ptr, err = UnsafePointerOrError(s) // <-- unsafe.Pointer, error >>>> if err != nil { >>>> return >>>> } >>>> return *(*T)(unsafe.Pointer(&ptr)) >>>> } >>>> >>>> func main() { >>>> var s C.StatusOr >>>> p := PointerOrError[*C.mystruct](s) >>>> _ = p >>>> } >>>> >>>> It's unfortunate, of course, that this would allow you to instantiate >>>> the function using a non-pointer as well, but it seems that's impossible >>>> to >>>> prevent statically? You can catch it dynamically by doing something akin to >>>> >>>> if k := reflect.TypeOf(*new(T)).Kind(); k != reflect.Pointer && k != >>>> reflect.UnsafePointer { >>>> panic("PointerOrError must be instantiated with pointer type") >>>> } >>>> >>>> Obviously, none of this is ideal, but maybe it's the best you can do - >>>> apart from generating code. >>>> >>>> On Sat, Apr 15, 2023 at 8:48 PM Jan <pfe...@gmail.com> wrote: >>>> >>>>> Re-factoring your example to use CGO, in a small main.go file: >>>>> >>>>> ``` >>>>> $ go run . >>>>> ./main.go:28:10: cannot use incomplete (or unallocatable) type as a >>>>> type argument: main._Ctype_struct_MyThing >>>>> $ cat main.go >>>>> package main >>>>> >>>>> // struct MyThing; >>>>> // typedef struct MyThing MyThing; >>>>> import "C" >>>>> import ( >>>>> "fmt" >>>>> "unsafe" >>>>> ) >>>>> import "flag" >>>>> >>>>> func PointerOrError[T *Q, Q any](s int) (t T, err error) { >>>>> var ptr unsafe.Pointer >>>>> ptr, err = UnsafePointerOrError(s) // <-- unsafe.Pointer, error >>>>> if err != nil { >>>>> return >>>>> } >>>>> t = (T)(ptr) >>>>> return >>>>> } >>>>> >>>>> func UnsafePointerOrError(v int) (unsafe.Pointer, error) { >>>>> return unsafe.Pointer(&v), nil >>>>> } >>>>> >>>>> func main() { >>>>> flag.Parse() >>>>> t, _ := PointerOrError[*C.MyThing](1) >>>>> fmt.Println(t) >>>>> } >>>>> ``` >>>>> >>>>> >>>>> On Saturday, April 15, 2023 at 8:41:28 PM UTC+2 Jan wrote: >>>>> >>>>>> Thanks! I hadn't realized that one could constraint T to be a pointer >>>>>> type by using a second type paramater Q, this in nice. >>>>>> >>>>>> But alas, it doesn't work. When I copy&pasted your code to mine, and >>>>>> used an undefined C type ... it complained of the same thing: >>>>>> >>>>>> ``` >>>>>> cannot use incomplete (or unallocatable) type as a type argument: >>>>>> gomlx/xla._Ctype_struct_StableHLOHolder >>>>>> ``` >>>>>> >>>>>> I'm using it in the following snipped of code: >>>>>> >>>>>> ``` >>>>>> func (comp *Computation) ToStableHLO() (*StableHLO, error) { >>>>>> if comp.IsNil() || comp.firstError != nil { >>>>>> return nil, errors.Errorf("Computation graph is nil!?") >>>>>> } >>>>>> statusOr := C.ConvertComputationToStableHLO(comp.cCompPtr) >>>>>> cPtr, err := PointerOrError[*C.StableHLOHolder](statusOr) >>>>>> if err != nil { >>>>>> return nil, errors.Wrapf(err, "failed conversion in >>>>>> Computation.ToStableHLO") >>>>>> } >>>>>> return NewStableHLO(cPtr), nil >>>>>> } >>>>>> ``` >>>>>> >>>>>> I suspect it doesn't allow matching Q to an incomplete type >>>>>> (`C.StableHLOHolder` in this example), same way as my original version :( >>>>>> >>>>>> I think your example in playground doesn't capture that -- the >>>>>> playground doesn't seem to allow CGO code (i tried this >>>>>> <https://go.dev/play/p/ZM14sQuK8iN?v=gotip.go?download=true>, but it >>>>>> didn't even try to compile). >>>>>> >>>>>> I mean it's not the end of the world, I can always cast it in the >>>>>> next line ... it's just one of those little things that would be >>>>>> "ergonomically" very nice if it worked :) >>>>>> >>>>>> >>>>>> >>>>>> On Saturday, April 15, 2023 at 3:02:14 PM UTC+2 jake...@gmail.com >>>>>> wrote: >>>>>> >>>>>>> What About: >>>>>>> >>>>>>> func PointerOrError[T *Q, Q any](s C.StatusOr ) (t T, err error) >>>>>>> >>>>>>> Seems to compile: https://go.dev/play/p/n4I-XkONj-O?v=gotip >>>>>>> >>>>>>> On Saturday, April 15, 2023 at 6:28:39 AM UTC-4 Jan wrote: >>>>>>> >>>>>>>> hi, >>>>>>>> >>>>>>>> This is a variation for a previous topic >>>>>>>> <https://groups.google.com/g/golang-nuts/c/h75BwBsz4YA/m/FLBIjgFBBQAJ>, >>>>>>>> >>>>>>>> but since there isn't a clear solution, I thought I would ask if >>>>>>>> anyone can >>>>>>>> think of a work around. >>>>>>>> >>>>>>>> I've been interacting a lot with C++ libraries from Go, and one of >>>>>>>> the commonly returned types is an abls::StatusOr >>>>>>>> <https://abseil.io/docs/cpp/guides/status>, for which I created a >>>>>>>> simple C wrapper that casts the error and value to a `char *` and >>>>>>>> `void *` >>>>>>>> respectively (dropping the type information in between C++ and Go). >>>>>>>> >>>>>>>> In Go I want to return the type information, so I defined a small >>>>>>>> generic function: >>>>>>>> >>>>>>>> // PointerOrError converts a StatusOr structure to either a >>>>>>>> pointer to T with the data >>>>>>>> // or the Status converted to an error message and then freed. >>>>>>>> func PointerOrError[T any](s C.StatusOr) (*T, error) { >>>>>>>> ptr, err := UnsafePointerOrError(s) // returns unsafe.Pointer, >>>>>>>> error >>>>>>>> if err != nil { >>>>>>>> return nil, err >>>>>>>> } >>>>>>>> return (*T)(ptr), nil >>>>>>>> } >>>>>>>> >>>>>>>> Now this doesn't work for my forward declared C++ types (most of >>>>>>>> them are just aliases to C++ objects) -- Go complaints with: `cannot >>>>>>>> use incomplete (or unallocatable) type as a type argument`, >>>>>>>> because `T` is incomplete indeed. >>>>>>>> >>>>>>>> But ... I will never instantiate `T`, I only care about `*T`, which >>>>>>>> is not incomplete. >>>>>>>> >>>>>>>> But there isn't a way to say a generics attribute is a pointer. So >>>>>>>> if I use the following: >>>>>>>> >>>>>>>> func PointerOrError2[T any](s C.StatusOr) (t T, err error) { >>>>>>>> var ptr unsafe.Pointer >>>>>>>> ptr, err = UnsafePointerOrError(s) // <-- unsafe.Pointer, error >>>>>>>> if err != nil { >>>>>>>> return >>>>>>>> } >>>>>>>> t = (T)(ptr) >>>>>>>> return >>>>>>>> } >>>>>>>> >>>>>>>> And instantiate it with a `PointerOrError2[*MyType](statusOr)` for >>>>>>>> instance, I get, as expected: >>>>>>>> >>>>>>>> cannot convert ptr (variable of type unsafe.Pointer) to type T >>>>>>>> >>>>>>>> Any suggestions to make this work ? >>>>>>>> >>>>>>>> I could probably craft something using the `reflect` package, but I >>>>>>>> was hoping for a smart (and likely faster?) generics solution. >>>>>>>> >>>>>>>> cheers >>>>>>>> Jan >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> -- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "golang-nuts" group. >>>>> To unsubscribe from this group and stop receiving emails from it, send >>>>> an email to golang-nuts...@googlegroups.com. >>>>> To view this discussion on the web visit >>>>> https://groups.google.com/d/msgid/golang-nuts/639d57d5-37da-424b-a137-41a7bca7e821n%40googlegroups.com >>>>> >>>>> <https://groups.google.com/d/msgid/golang-nuts/639d57d5-37da-424b-a137-41a7bca7e821n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>> . >>>>> >>>> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com. >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/55228baf-9314-4e4c-9455-677dc9ed73b6n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/golang-nuts/55228baf-9314-4e4c-9455-677dc9ed73b6n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3a0f64dc-0eb4-45b7-b585-f379270ea19fn%40googlegroups.com.