What is the best way to call into Swift from C?

2020-08-09 10:20发布

问题:

Calling into C from Swift is pretty simple, however I'm looking into making a bi-directional wrapper in C, so my C has to call Swift functions.

Right now, I can do this by declaring function pointers in C, and having my C functions call them after the Swift side has set them up to call code in Swift.

My C header file:

typedef void (*callback_t)(void);

void callBackIntoSwift( callback_t cb );

My C implementation file:

#include "stuff.h"
#include <stdio.h>

void callBackIntoSwift( callback_t cb )
{
    printf( "Will call back into Swift\n" );
    cb();
    printf( "Did call back into Swift\n" );
}

After including my C header file in the bridging header, I can do the following on the Swift side:

let cb: callback_t = {
    someKindOfSwiftFunction()
}

callBackIntoSwift( cb )

Or even:

callBackIntoSwift {
    someKindOfSwiftFunction()
}

Is there a better way to do this, where function pointers and callbacks are not needed? I'd like to let the C-side call someKindOfSwiftFunction directly … but when I try to apply @convention (c) to function declarations I get the message that the attribute can only be applied to types, and not declarations.

Any ideas or codebases in e.g. Github I can take a look at?

回答1:

According to Joe Groff:

There’s no official way yet. Aside from name mangling, Swift functions use a different calling convention from C. Unofficially, if you’re willing to deal with more than the usual amount of code breakage and compiler bugs, there’s an unofficial attribute @_cdecl that does this:

@_cdecl("mymodule_foo")
func foo(x: Int) -> Int {
  return x * 2
}

which you can then call from C:

#include <stdint.h>

intptr_t mymodule_foo(intptr_t);

intptr_t invoke_foo(intptr_t x) {
  return mymodule_foo(x);
}


回答2:

You can do something like this:

FileSwift.swift

public class SwiftTest: NSObject {
    @objc public static func testMethod() {
        print("Test")
    }
}

FileSwiftWrapper.h

void SwiftFunctionWrapper();

FileSwiftWrapper.m

#import "ProductModuleName-Swift.h"

void SwiftFunctionWrapper() {
      [SwiftTest testMethod];
}