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 <pfe...@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...@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/3a0f64dc-0eb4-45b7-b585-f379270ea19fn%40googlegroups.com.

Reply via email to