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.

Reply via email to