Swift: передача неинициализированной структуры C в импортированную функцию C
Я знаю об этом ответе, но это не одно и то же - это передача указателя для инициализации с выделением.
Я взаимодействую с библиотекой C, которая имеет следующее определение структуры:
typedef struct myStruct { unsigned char var [50]; } myStruct;
Есть функция, которой вы передаете адрес структуры - обычно это стек, а не куча, таким образом:
myStruct mine;
initMyStruct(&mine);
Это инициализирует содержимое структуры - вызывающая сторона не знает внутренний формат памяти, следовательно, запутывание.
В Swift я создаю класс, который будет инкапсулировать структуру и интерфейс с другими функциями C, которые работают с ним.
class MyClass {
var mine : myStruct
init() {
initMyStruct(&mine)
// Error: Variable 'self.msg' passed by reference before being initialized
}
}
Я не могу на всю жизнь понять, как инициализировать структуру, или использовать точку вместо этого, если это альтернатива.
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
Обратите внимание, что это передача адреса уже распределенной (основанной на стеке) структуры функции, которая была импортирована как
CInt initMyStruct(str: CMutablePointer<myStruct>)
Он НЕ передает указатель, который будет выделен рассматриваемой библиотекой C, что, как представляется, является более распространенным требованием и на него есть ответ в другом месте.
В настоящее время Swift также не поддерживает массивы фиксированного размера.
Я не вижу, как удовлетворить инициализацию структуры или заставить Свифта думать, что это будет сделано вызовом.
Любая помощь с благодарностью!
6 ответов
Во-первых, давайте определим наш C-код для тестирования:
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;
}
}
В Swift у нас есть два варианта:
1. Структура в стеке
var my_s: my_struct = ...
Однако мы должны как-то его инициализировать. Каждая структура имеет инициализатор по умолчанию
var my_s: my_struct = my_struct(buffer: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
Обратите внимание, что в этом случае buffer[10]
был переведен на Swift как 10-tuple
,
Теперь мы можем позвонить:
my_struct_init(&my_s)
print("Buffer: \(my_s.buffer)") // Buffer: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
Однако чем сложнее структура, тем сложнее использовать инициализатор по умолчанию.
2. Строить на куче
Это похоже на использование malloc
а также free
в С:
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()
Объедините оба подхода
Следующая функция инициализирует любую структуру:
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)
Верьте или нет, вы можете инициализировать C Struct так же, как Swift Struct. XCode даже автоматически заполняет имена участников для вас!
В этом частном случае
var when = timespec(tv_sec:0, tv_nsec:0)
Будет устанавливать when
эпохи
А вот магический инициализатор, который дает вам пустую структуру:
func blankof<T>(type:T.Type) -> T {
var ptr = UnsafePointer<T>.alloc(sizeof(T))
var val = ptr.memory
ptr.destroy()
return val
}
Приведенный выше пример будет:
var when = blankof(timespec)
Начиная с Swift 1.2 (бета-версия Xcode 6.3), импортированные структуры C теперь имеют инициализатор по умолчанию в Swift, который инициализирует все поля структуры в ноль.
В твоем случае:
class MyClass {
var mine = myStruct() // <-- Initializes all elements to zero
init() {
initMyStruct(&mine) // <-- No error anymore because `mine` is initialized
}
}
У меня та же проблема с библиотекой C, и я задал этот вопрос на форумах Apple. Пока без разрешения.
Чтобы обойти эту проблему, не изменяя исходную библиотеку C, я создал функции Create/Destroy в 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);
Это не использует преимущества ARC, но, по сути, позволяет обойти проблему без изменения исходной библиотеки.
Как насчет этого
var mine : UnsafePointer<myStruct> = nil
initMyStrict(&mine)
Я не знаю, что происходит внутри initMyStruct... но предположим, что это функция, которая принимает в качестве параметра строку, которую вы хотите скопировать в поле var
Я никогда не использовал swift... но в этом коде вы можете использовать свою структуру в качестве члена класса C++...
Может быть, вы могли бы использовать этот класс, который вместо этого просто инкапсулирует структуру?
#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);
}
Если он использует некоторые функции cstring, это может вызвать некоторые проблемы, поскольку указатель имеет тип unsigned char*, а не char*...