coorfang
(kef)
1
Hi all,
I have a following opaque type defined in C code
my_client.h
typedef struct MyClient MyClient;
// to init
void init_my_client(struct MyClient** client_ptr) {
MyClient* client = init_impl()
// when calling in swift, this doesn't work
*client_ptr = client
}
// to destroy
void destroy(struct MyClient* client_ptr) {
free(client_ptr)
}
When I imported this header file into swift, it infers type struct MyClient** into UnsafeMutablePointer<OpaquePointer?>?
So I tried the following way to init it, when I assign value to this pointer in C, it reports Exit code 11...
MyClient.swift
public class MyClient {
let clientPtr: UnsafeMutablePointer<OpaquePointer?>?
init() {
init_my_client(clientPtr)
// the unexpected exit happens inside this C function, during assigning my client pointer to clientPtr
}
deinit() {
if let ptr = clientPtr {
if let innerPtr = ptr.pointee {
destroy(innerPtr)
clientPtr = nil
}
}
}
}
I am very new to swift, could anyone here help to point out what's the problem exactly?
I have a felling on that, before passing clientPtr into init_my_client, it's not been initialized (with value nil?), but I am not aware of the right way to do so.
Thanks
coorfang
(kef)
2
Well, 2 minutes after I posted this question, I figured it out.
the solution is: changing clientPtr type to be OpaquePointer?, and passing &clientPtr into the init_my_client function.
Accordingly, deinit should be changed as well.
The final swift code is:
public class MyClient {
let clientPtr: OpaquePointer?
init() {
init_my_client(&clientPtr)
}
deinit() {
if let inner = clientPtr {
destroy(innerPtr)
clientPtr = nil
}
}
}
1 Like
tera
3
I'd do this in C header:
typedef struct ClientStruct *Client;
Client newClient(void);
void deleteClient(Client);
and this in swift:
class SwiftClient {
let client /* : Client! */ = newClient()
deinit {
if let client = client {
deleteClient(client)
}
}
}
coorfang
(kef)
4
Thanks for the reply.
This seems to work, but the reason why I'd passing in the pointer object is that, in the real implementation, there is a return code on both newClient and deleteClient indicating if the operation was done correctly.
tera
5
That you can pass as BOOL foo(...., NSError**) from C and it would be converted into try/catch in swift. (Updated below.)
coorfang
(kef)
6
If I understood correctly, the c code needs to be modified (adding NSError** param) to achieve this?
The fact is this c library is designed to be multi-language compatible not only for swift..
or you mean, by only changing the return type to boolean on C function, swift can then perform some magic to implement try .. catch ?
tera
7
Actually scratch the above, unless it works for C methods (vs obj-c methods), which is probably not the case.
This may work for you, C header:
typedef struct ClientStruct *Client;
Client newClient(NSInteger* errorCode);
void deleteClient(Client, NSInteger* errorCode);
swift:
class SwiftClient {
let client: Client!
init() {
var errorCode = 0
client = newClient(&errorCode)
}
deinit {
if let client = client {
var errorCode = 0
deleteClient(client, &errorCode)
}
}
}
still no "opaque" stuff.
tera
8
Yep, just I don't know if there is a way to express this machinery in pure C. If to do this via Obj-C bridge that would be:
@interface IClient: NSObject
+ (Client)newClient:(NSError**)error;
+ (BOOL)deleteClient:(Client)client error:(NSError**)error;
@end
@implementation IClient
+ (Client)newClient:(NSError**)error {
*error = [NSError errorWithDomain:@"domain" code:-1 userInfo:nil];
return nil;
}
+ (BOOL)deleteClient:(Client)client error:(NSError**)error {
*error = [NSError errorWithDomain:@"domain" code:-1 userInfo:nil];
return NO;
}
@end
It shows how you return error. On success assign error to nil and return client/YES correspondingly.
swift side becomes:
client = try! IClient.newClient()
....
try! IClient.delete(client)
or:
do {
client = try IClient.newClient()
} catch {
print(error)
}
coorfang
(kef)
9
Looks cool, thanks a lot tera, I ll take it as my reference!