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.wagner...@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 <pfei...@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+unsubscr...@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/CAEkBMfFtC-e28_GriiMvrvttROmsti22jr30h5Db56SiXmVPhw%40mail.gmail.com.