Using C functions as Swift struct methods

I've bridged my C struct into Swift and i can use it.
However I have C routines for manipulating the struct. How can I use those C routines in Swift as if they were methods of a Swift struct?
e.g.
typedef struct { int a; } Noynk;
void Noynk_adder(Noynk *noynk, int d) { noynk->a += d };
How can I say in Swift:
noynk.adder (d)
?

Add __attribute__((swift_private)) to the function declarations. (If you're importing Apple Foundation, you can use the NS_REFINED_FOR_SWIFT macro instead.) The functions will now be imported to Swift with double underscore prefixes.

On the Swift side, declare the method in an extension on your type, calling through to the original function:

extension Nonyk {
    mutating func adder(_ d: Int) {
        __Nonyk_adder(self, d)
    }
}

EDIT: I missed originally that your function takes a pointer to the struct. In that case, declare a typedef for the pointer type (e.g. typedef Nonyk * NonykRef;) and make the Swift extension on that type instead of on the struct itself.

4 Likes

The swift-name attribute allows to specify how a C function should be imported to Swift. This includes the ability to import C functions as Swift (type or instance) methods. For the details, see SE-0044 Import as member.

In your case you can declare

typedef struct { int a; } Noynk;

void Noynk_adder(Noynk *noynk, int d)
__attribute__((swift_name("Noynk.add(self:_:)")));

in the C header file, and then call it as a member function in Swift:

var noynk = Noynk()
print(noynk.a) // 0
noynk.add(123) // <--- HERE!
print(noynk.a) // 123
8 Likes

If you weren't reading through all of the SE proposals, how would you ever figure something like that out? :o

I happened to be familiar with this because I reported a bug some time ago: Import C functions as subscript methods, Import C functions as subscript methods (follow-up) – which, by the way, hasn't been fixed until now.

There’s a great WWDC video from @beccadax this year that goes into some detail about these attributes. Refine Objective-C frameworks for Swift - WWDC20 - Videos - Apple Developer

10 Likes

I’d missed that one. Great talk, @beccadax!

1 Like

If you weren't reading through all of the SE proposals, how would you
ever figure something like that out?

I tend to steal liberally from the Apple headers. In this case CoreGraphics is a great source of examples. The mappings aren’t in the headers per se, rather they’re in CoreGraphics.apinotes, but the core concepts all apply.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

What about class methods? For instance in C I might have

typedef struct { int x; } Foo;
Foo Foo_maker (int x);
**Foo Foo_zero (); **

How do I use that in Swift like so?

let foo : Foo = Foo(123)
let foozero = Foo.zero()

I'm getting fun errors like: partial application of 'mutating' method is not allowed
and
instance member 'zero' cannot be used on type 'Foo'; did you mean to use a value of this type instead?

See “Import as init” and “Import as static computed property” in SE-0044 Import as member. In your case that would be

typedef struct { int x; } Foo;

Foo Foo_maker (int x)
__attribute__((swift_name("Foo.init(_:)")));

Foo Foo_zero(void)
__attribute__((swift_name("getter:Foo.zero()")));

which can be used from Swift as

let foo = Foo(123)
let bar = Foo.zero
1 Like

The init works well. Thanks for that. But I'm finding that the .zero method is being rejected in the same way as before.

I can only say that I tested it and it compiled and ran without problems. Did you use the exact code that I suggested, or a variation? Note that the swift_name of static properties does not contain “self”.

1 Like