Re: [go-nuts] Re: Pointer constraint to generics

2023-04-16 Thread Jan
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  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, 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  
>>> 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())
 }

 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  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
>   

Re: [go-nuts] Re: Pointer constraint to generics

2023-04-16 Thread 'Axel Wagner' via golang-nuts
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  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, 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 
>> 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())
>>> }
>>>
>>> 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  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(), 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 

Re: [go-nuts] Re: Pointer constraint to generics

2023-04-15 Thread Jan
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, 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  
> 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())
>> }
>>
>> 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  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(), 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 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 

Re: [go-nuts] Re: Pointer constraint to generics

2023-04-15 Thread 'Axel Wagner' via golang-nuts
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, 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 
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())
> }
>
> 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  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(), 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 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
>>> , 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
> 

Re: [go-nuts] Re: Pointer constraint to generics

2023-04-15 Thread 'Axel Wagner' via golang-nuts
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())
}

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  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(), 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 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
>> , 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
 ,
 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
 , 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`, 

[go-nuts] Re: Pointer constraint to generics

2023-04-15 Thread Jan
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(), 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 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 
> , 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 
>>> , 
>>> 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 
>>> , 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 

[go-nuts] Re: Pointer constraint to generics

2023-04-15 Thread Jan
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 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 
, 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 
>> , 
>> 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 
>> , 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/d5c7ac8c-5db7-463e-b87b-c8d2ad6c727en%40googlegroups.com.


[go-nuts] Re: Pointer constraint to generics

2023-04-15 Thread jake...@gmail.com
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 
> , 
> 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 
> , 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/b9e377dd-4ef8-4cdf-8c36-80cb086910b1n%40googlegroups.com.