Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Pass Uninitialized C Structure to Imported C function

I'm aware of this answer, but this is not the same thing - thats passing a pointer to be initialised with an allocation.

I'm interfacing with a C library that has the following structure definition:

typedef struct myStruct { unsigned char var [50]; } myStruct;

There is a function to which you pass the address of the structure - normally stack based not heap, thus:

myStruct mine;
initMyStruct(&mine);

This initialises the content of the struct - the caller does not know the internal format of the memory, hence the obfuscation.

In Swift, I'm creating a class which will encapsulate the structure and interface with other C functions which operates on it.

class MyClass {

    var mine : myStruct

    init() {

        initMyStruct(&mine)
        // Error: Variable 'self.msg' passed by reference before being initialized

    }

}

I can't for the life of me work out how to initialise the structure, or use a point instead if that is an alternative.

mine = myStruct()
// Fails because you aren't providing the value of the member 'var'

mine = myStruct((CUnsignedChar(), CUnsignedChar(), /*... repeat to 50 */))
// Cannot find an overload for 'init' that accepts the arguments

Please note, this is passing the address of an already allocated (stack based) structure to a function that has been imported as

CInt initMyStruct(str: CMutablePointer<myStruct>)

It is NOT passing a pointer to be allocated by the C library in question which seems to be a more common requirement and is answered elsewhere.

Swift currently doesn't support fixed size arrays either.

I can't see how to satisfy the initialisation of the structure or make Swift think that it is going to be done so by the call.

Any help greatly appreciated!

like image 708
Dave Meehan Avatar asked Jun 10 '14 16:06

Dave Meehan


2 Answers

First, let's define our C code for testing:

typedef struct my_struct {
    unsigned char buffer[10];
} my_struct;

void my_struct_init(my_struct *my_s) {
    for (int i = 0; i < 10; i++) {
        my_s->buffer[i] = (char) i;
    }
}

In Swift, we have two options:

1. Struct on the stack

var my_s: my_struct = ...

However, we have to initialize it somehow. Every struct has a default initializer

var my_s: my_struct = my_struct(buffer: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0))

Note that in this case the buffer[10] has been translated to Swift as a 10-tuple.

Now we can call:

my_struct_init(&my_s)
print("Buffer: \(my_s.buffer)") // Buffer: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

However, the more complex is the struct, the more difficult is to use the default initializer.

2. Struct on the heap

This is similar to using malloc and free in C:

var my_s_pointer = UnsafeMutablePointer<my_struct>.allocate(capacity: 1)
print("Buffer: \(my_s.buffer)") // Buffer: (some random values)

my_struct_init(my_s_pointer)
print("Buffer: \(my_s_pointer.memory.buffer)") // Buffer: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

my_s_pointer.deallocate()

Combine both approaches

The following function will initialize any struct:

func initStruct<S>() -> S {
    let struct_pointer = UnsafeMutablePointer<S>.allocate(capacity: 1)

    let struct_memory = struct_pointer.pointee
    struct_pointer.dealloate()

    return struct_memory
}

var my_s: my_struct = initStruct()
my_struct_init(&my_s)
like image 74
Sulthan Avatar answered Sep 30 '22 18:09

Sulthan


Believe it or not, you can initialize C Struct just like Swift Struct. Xcode even autocompletes member names for you!

C Struct as Swift Struct

In this Particular case,

var when = timespec(tv_sec:0, tv_nsec:0)

Will set when to epoch.

And here is a magical initializer that gives you any blank struct:

func blankof<T>(type:T.Type) -> T {
    var ptr = UnsafePointer<T>.alloc(sizeof(T))
    var val = ptr.memory
    ptr.destroy()
    return val
}

The above example would be:

var when = blankof(timespec)
like image 21
dankogai Avatar answered Sep 30 '22 17:09

dankogai