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 *...