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 <pfei...@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+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
> <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/CAEkBMfEzDBApz6peLs5NCnnePvPpUUyhH%3Dr8WJNTnh9DjDFoRg%40mail.gmail.com.

Reply via email to