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 не поддерживает массивы с фиксированным размером.
Я не вижу, как удовлетворить инициализацию структуры или заставить Swift думать, что это будет сделано по вызову.
Любая помощь очень ценится!
Ответы
Ответ 1
Сначала давайте определим наш 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)
println("Buffer: \(my_s.buffer)") // Buffer: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
Однако чем сложнее структура, тем сложнее использовать инициализатор по умолчанию.
2. Структура в куче
Это похоже на использование malloc
и free
в C:
var my_s_pointer = UnsafeMutablePointer<my_struct>.alloc(1)
println("Buffer: \(my_s.buffer)") // Buffer: (some random values)
my_struct_init(my_s_pointer)
println("Buffer: \(my_s_pointer.memory.buffer)") // Buffer: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
my_s_pointer.destroy()
Объедините оба подхода
Следующая функция инициализирует любую структуру:
func initStruct<S>() -> S {
let struct_pointer = UnsafeMutablePointer<S>.alloc(1)
let struct_memory = struct_pointer.memory
struct_pointer.destroy()
return struct_memory
}
var my_s: my_struct = initStruct()
my_struct_init(&my_s)
Ответ 2
Верьте или нет, вы можете инициализировать C Struct так же, как Swift Struct. Xcode даже автозаполняет имена участников для вас!
![C Struct as Swift Struct]()
В этом частном случае
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)
Ответ 3
У меня такая же проблема с библиотекой 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, но в аренду он проходит мимо проблемы без изменения исходной библиотеки.
Ответ 4
По состоянию на Swift 1.2 (Xcode 6.3 beta),
импортированные C-структуры теперь имеют инициализатор по умолчанию в Swift, который инициализирует все поля структуры равными нулю.
В вашем случае:
class MyClass {
var mine = myStruct() // <-- Initializes all elements to zero
init() {
initMyStruct(&mine) // <-- No error anymore because `mine` is initialized
}
}
Ответ 5
Как насчет этого
var mine : UnsafePointer<myStruct> = nil
initMyStrict(&mine)
Ответ 6
Я не знаю, что происходит внутри initMyStruct... но предположим, что это функция, которая принимает в качестве параметра строку, которую вы хотите скопировать, в поле var
Я никогда не использовал swift... но на этом коде здесь вы могли бы создать свою структуру как член класса С++...
Может быть, вы можете использовать этот класс, который просто инкапсулирует структуру вместо этого?
#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, это может вызвать некоторые проблемы, поскольку указатель без знака char *, а не char *...