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+unsubscr...@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.