I figured out what was going on. The swift interop layer isn't smart enough to understand the tricks the author of czmq plays on the compiler toolchain in the name of better code, so we have to do some C wrapping to hide it from swift.
The easy bits:
- Remove the
#include <czmq.h>
line from your bridging header
- Add a line importing another header of your choice (I chose
szsocket.h
because it's the swift zsocket wrapper)
- Create a corresponding .c file and
#import <czmq.h>
The harder bits:
You need to recreate any structures in your header that are used by the library. In my case the zsock_t
structure which was defined as follows:
typedef struct zsock_t {
uint32_t tag; // Object tag for runtime detection
void *handle; // The libzmq socket handle
char *endpoint; // Last bound endpoint, if any
char *cache; // Holds last zsock_brecv strings
int type; // Socket type
size_t cache_size; // Current size of cache
} zsock_t;
We create a simple wrapper like so:
typedef struct szsock_t {
uint32_t tag; // Object tag for runtime detection
void *handle; // The libzmq socket handle
char *endpoint; // Last bound endpoint, if any
char *cache; // Holds last zsock_brecv strings
int type; // Socket type
size_t cache_size; // Current size of cache
} szsock_t;
You will also need to recreate any typedefs used in those structures. In this case, handily no others. These all go in the new header (.h) file
You then need to wrap every function in the library that accepts one of these structures. We take zsock_new
function as an example.
First we need to predeclare our version in the header to avoid any zsock types. We just replace every occurrence of zsock
with szsock
(emacs can help with this):
szsock_t *szsock_new (int type, const char *filename, size_t line_nbr);
Next we need to create the wrapper function in the .c file:
szsock_t *szsock_new (int type, const char *filename, size_t line_nbr) {
return (szsock_t *) zsock_new(type, filename, line_nbr);
}
Notice how we cast between zsock_t
and szsock_t
and use the internal zsock functions. It's safe to do so because the swift compiler won't be reading it, just the c compiler.
Next up, there were a bunch of varargs functions. This worked for me:
int szsock_bind (szsock_t *self, const char *format, ...) {
int ret;
va_list args;
va_start(args, format);
ret = zsock_bind( (zsock_t *) self, format, args);
va_end(args);
return ret;
}
Good luck to anyone reading wrapping a library in swift!