Swift: Pass Uninitialized C Structure to Imported C function

时间秒杀一切 提交于 2019-12-28 04:23:30

问题


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!


回答1:


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)



回答2:


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

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)



回答3:


As of Swift 1.2 (Xcode 6.3 beta), imported C structs now have a default initializer in Swift, which initializes all of the struct's fields to zero.

In your case:

class MyClass {

    var mine = myStruct() // <-- Initializes all elements to zero

    init() {
        initMyStruct(&mine) // <-- No error anymore because `mine` is initialized
    }
}



回答4:


I have the same issue with a C library and have asked this question over on Apple's Forums. So far no resolution.

To work around the issue without modifying the original C library, I have created Create/Destroy functions in Bridging.c/.h

Bridging.h

typedef struct myStruct { unsigned char var [50]; } myStruct;
myStruct * CreateMyStruct();
void DestroyMyStruct(myStruct **s);
void DoSomething(myStruct *s);

Bridging.c

myStruct * CreateMyStruct() {
    myStruct * mine = malloc(sizeof(myStruct));
    if (mine)
        initMyStruct(mine);
    return mine;
}

void DestroyMyStruct(myStruct **s) {
    if (*s)
        free(*s);
    *s = NULL;  // set the swift variable to nil
}

void DoSomething(myStruct *s)
{
    // ...
}

Example.swift

var mine : UnsafePointer<myStruct> = CreateMyStruct();
DoSomething(mine);
DestroyMyStruct(&mine);

This does not take advantage of ARC, but at lease it gets past the issue without modifying the original library.




回答5:


How about this

var mine : UnsafePointer<myStruct> = nil
initMyStrict(&mine)



回答6:


I don't know what is going on inside initMyStruct... but suppose it is a function that takes as parameter the string you want to copy to field var

I have never used swift... but on this code here you could have your struct as a c++ class member...
Maybe you could use this class that just encapsulates the struct instead?

#include <cstdio>

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

void initMyStruct(myStruct *mine, char *ini)
{
    int i = 0;
    do
    {
        mine->var[i] = (unsigned char) ini[i];
    } while(ini[i++]);
}

class MyClass
{
   public:
   myStruct st;

   MyClass(char *parameter)
   {
        initMyStruct(&st, parameter);
   }

};

int main()
{
    MyClass c("Hakuna Matata!");
    printf("%s\n", c.st.var);
}

If it uses some cstring functions it might cause some problems because the pointer is unsigned char* and not char*...



来源:https://stackoverflow.com/questions/24146488/swift-pass-uninitialized-c-structure-to-imported-c-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!