Skip to content

Ability to create an opaque type with methods #4638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
daurnimator opened this issue Mar 5, 2020 · 3 comments
Closed

Ability to create an opaque type with methods #4638

daurnimator opened this issue Mar 5, 2020 · 3 comments
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@daurnimator
Copy link
Contributor

I'm manually wrapping a C API which has opaque types. I'd like to be able to add methods to the opaque type rather than using free functions or wrapping it in a struct.

Wrapping a pointer in a struct has issues when it comes to identity: getPointer() == myThing is no longer true (instead the user needs to do something like: getPointer() == myThing.real_ptr)

@daurnimator daurnimator added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Mar 5, 2020
@ghost
Copy link

ghost commented Mar 5, 2020

I have no experience with wrapping C APIs, but if I understand you correctly, a variant of distinct types (#1595) might work really well for something like this. If distinct becomes a keyword where a trailing namespace (if present) routes all unified syntax calls to the distinct namespace instead of the original type. This could be very useful for creating "methods" on primitive types and arrays as well.

There is some overlap between this and the #1170 proposal, but here there wouldn't be the same ambiguity to which namespace a method belongs to.

// distinct type with (optional) namespace attached
const A = distinct(TypeToWrap){ // TypeToWrap can be a primitive, container or opaque type

  fn methodOn_TypeToWrap(self: @This()) void{
    // ...
  }
};

const a : A = @as(A,TypeToWrap.init());
a.methodOn_TypeToWrap(); // unified call syntax routed to namespace of distinct type A
a.origMethod(); // syntax error. use `TypeToWrap.origMethod(a)` instead

// no namespace attached. Unified syntax calls routed to TypeToWrap
const B = distinct(TypeToWrap); 

The original intent of distinct types would of course apply, with the compiler being able to helpfully differentiate between types that at runtime are indistinguishable.

@andrewrk andrewrk added this to the 0.7.0 milestone Mar 7, 2020
@ifreund
Copy link
Member

ifreund commented Sep 24, 2020

I've been working on some idiomatic libwayland bindings recently and being able to define methods for opaque types would make the bindings both easier to write and more ergonomic to use. Currently my pattern looks like this:

const Display = struct {
    const Impl = @Type(.Opaque);
    impl: *Impl,
    
    extern fn wl_display_get_fd(display: *Impl) c_int;
    pub fn getFd(display: Display) os.fd_t {
        return wl_display_get_fd(display.impl);
    }
};

For this basic use case, wrapping the opaque pointer in a struct works alright, but for more complex use-cases such as registering callbacks it quickly becomes unwieldy. The user of libwayland is asked to supply a function with the following signature:

fn (data: ?*c_void, display: *Display.Impl, id: u32) callconv(.C) void

To provide a nice api and "hide" the fact that the Display struct only exists to wrap the Display.Impl opaque pointer I now need to wrap the callback in another function, leading to uneeded complexity.

As for how best to expose this feature on a language level, I propose adding an opaque keyword that could be used like so:

const Display = opaque {
    extern fn wl_display_get_fd(display: *Display) c_int;
    pub fn getFd(display: *Display) os.fd_t {
        return wl_display_get_fd(display);
    }
};

I think this would be better than opaque struct { ... } or similar as opaque types do not have fields in their definition and may only be referred to by pointer, which makes them significantly different from normal/packed/extern structs.

@andrewrk
Copy link
Member

andrewrk commented Oct 7, 2020

Dupe of #5574

@andrewrk andrewrk closed this as completed Oct 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

3 participants