问题
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