Передача быстрой строки в C - PullRequest
0 голосов
/ 01 мая 2019

Я довольно новичок в Swift, и у меня мало опыта работы с C.

Я пытаюсь написать функцию на C, которая получит строку Swift, с которой я потом смогу что-то сделать.Проблема в том, что я не уверен на 100%, какой тип должен быть в Swift, чтобы Си нравилось тому, что он видит.

До сих пор я нашел несколько примеров в стеке, которые кажутся хорошими отправными точками, но некоторые примеры кажутся устаревшими для текущей версии Swift.

Я впервые начал с использования этого примера, чтобы C и Swift общались друг с другом: Swift, вызов C, вызов Swift? Затем я взял это и попытался обновить функцию Swift, чтобы она возвращала строкукакой-то.Я понимаю, что это должен быть тип возврата UTF-8, но я не уверен, как правильно отправлять сообщения.Я смотрел на Как передать строку Swift в функцию переменного тока? , Как преобразовать строку в UnsafePointerи длиной и Как преобразовать строку в строку Unicode (UTF-8) в Swift? , но ни одна из них на самом деле не работает для решения.Или я просто набираю это неправильно.Пока что самое близкое, что я могу получить к возврату чего-либо, это следующее:

В Swift мой ViewController:

import UIKit

class ViewController: UIViewController {

    @_silgen_name("mySwiftFunc") // give the function a C name
    public func mySwiftFunc(number: Int) -> [CChar]
    {
        print("Hello from Swift: \(number)")

        let address: String = "hello there";
        let newString = address.cString(using: String.Encoding.utf8)
        return newString!
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        blah()

    }
}

А в C заголовок выглядит так:

#ifndef cfile_h
#define cfile_h

#include <stdio.h>

const char * mySwiftFunc(int);
int blah(void);

#endif /* cfile_h */

И источник выглядит так:

#include "cfile.h"

int blah() {

    const char * retVal = mySwiftFunc(42); // call swift function
    printf("Hello from C: %s", retVal);

    return 0;
}

Существует файл заголовка моста, который просто имеет #include "cfile.h".Очевидно, что в первом примере еще много остатков, и они будут вычищены позже.

Что нужно изменить, чтобы сделать эту работу?Прямо сейчас консоль выплевывает

Hello from Swift: 42
Hello from C: (B\214

1 Ответ

2 голосов
/ 01 мая 2019

Эквивалент Swift const char * равен UnsafePointer<CChar>?, так что это верное возвращаемое значение. Тогда вам нужно подумать об управлении памятью. Один из вариантов заключается в том, чтобы выделить память для строки C в функции Swift и оставить ее вызывающей стороне, чтобы в конце концов освободить память:

public func mySwiftFunc(number: Int) -> UnsafePointer<CChar>? {
    print("Hello from Swift: \(number)")

    let address = "hello there"
    let newString = strdup(address)
    return UnsafePointer(newString)
}

передает строку Swift в strdup(), так что (временное) представление строки C создается автоматически. Эта строка C затем дублируется. Вызывающая функция C должна освободить эту память, когда она больше не нужна:

int blah() {
    const char *retVal = mySwiftFunc(42);
    printf("Hello from C: %s\n", retVal);
    free((char *)retVal);
    return 0;
}

⚠️ НО: Обратите внимание, что в вашем коде больше проблем:

  • mySwiftFunc() - это метод экземпляра класса, и поэтому имеет неявный аргумент self, который игнорируется вызывающей функцией C. Это может сработать случайно или вызвать странные сбои.
  • @_silgen_name не следует использовать за пределами стандартной библиотеки Swift, см. в этом обсуждении на форуме Swift.
  • Немного лучшей альтернативой является @_cdecl, но даже это официально не поддерживается. @_cdecl может использоваться только с глобальными функциями.
  • Как правило, вызов функций Swift непосредственно из C официально не поддерживается, см. это обсуждение на форуме Swift по причинам и возможным альтернативам.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...