【问题标题】:Swift: Pass Uninitialized C Structure to Imported C functionSwift:将未初始化的 C 结构传递给导入的 C 函数
【发布时间】:2014-07-31 12:58:17
【问题描述】:

我知道this answer,但这不是一回事 - 那是传递一个指针以使用分配进行初始化。

我正在与具有以下结构定义的 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 认为它会通过调用来完成。

非常感谢任何帮助!

【问题讨论】:

  • 你能用一些虚拟信息初始化它,然后再覆盖它吗?
  • 这就是整个问题 - 我无法弄清楚它想要什么语法(如果有的话)作为 unsigned char [50] 的初始化。似乎没有什么是可以接受的。如果我无法初始化它,我想要一种方法来告诉它忽略未初始化的传递引用。我试过编译器选项无济于事。感谢您的帮助。
  • 你真的需要使用 initMyStruct 吗? initMyStruct 里面是什么?
  • @nightshade 里面有什么并不重要,它对其他 C 库函数很重要——内部状态数据。事实上,我知道它被强制转换为联合,因此 50 个字节只是为不同的需求分配足够的存储空间,具体取决于代码后面发生的情况。但这对于 Swift 的语义问题并不重要。

标签: c macos pointers struct 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)
print("Buffer: \(my_s.buffer)") // Buffer: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

但是,结构越复杂,使用默认初始化器就越困难。

2.堆上结构

这类似于在 C 中使用 mallocfree

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)

【讨论】:

  • 两个问题:1)不应该是UnsafePointer&lt;S&gt;.alloc(1)吗?据我了解,大小指定了所需的元素数量,而不是字节。 - 2) var struct_memory = struct_pointer.memory 是否以某种方式保留了内存?或者释放struct_pointer.destroy()中的内存后如何使用它?
  • @MartinR 1) 我不确定。当我写这篇文章时,该方法没有记录。 2)如果它是一个结构,它是一个值类型,所以它被复制。也许我应该为S 添加一些约束以排除对象类型。
  • 哦,好吧,我不能添加约束来排除对象类型。我可以排除值类型(S : AnyObject),但反之则不行......目前,我也很难区分指针类型的.dealloc.destroy。可惜它根本没有记录。
  • UnsafePointer 应该是 UnsafeMutablePointer
  • .destroy() 应该是.dealloc(1),否则会泄露。
【解决方案2】:

信不信由你,你可以像 Swift Struct 一样初始化 C 结构体。 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)

【讨论】:

  • 我得到 UnsafePointer.Type' 没有名为 'alloc' 的成员"
【解决方案3】:

从 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
    }
}

【讨论】:

    【解决方案4】:

    我对 C 库有同样的问题,并在 Apple 的论坛上提出了这个问题。目前还没有解决办法。

    为了在不修改原始 C 库的情况下解决此问题,我在 Bridging.c/.h 中创建了 Create/Destroy 函数

    桥接.h

    typedef struct myStruct { unsigned char var [50]; } myStruct;
    myStruct * CreateMyStruct();
    void DestroyMyStruct(myStruct **s);
    void DoSomething(myStruct *s);
    

    桥接.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,但至少在不修改原始库的情况下解决了问题。

    【讨论】:

      【解决方案5】:

      这个怎么样

      var mine : UnsafePointer<myStruct> = nil
      initMyStrict(&mine)
      

      【讨论】:

        【解决方案6】:

        我不知道 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*...

        【讨论】:

        • 将结构包装在 C++ 或 Obj-C 类中可能是一种让 Swift 编译器认为它已初始化的解决方案,但这是我宁愿不使用的抽象层。如果是这样,为什么不直接用 C++/Obj-C 编写整个解决方案并允许它导入到 Swift 中呢?似乎不是重点,但它可能反映了编译器的当前状态
        • 你有没有试过在结构体中添加一个默认构造函数呢?也许当你声明一个变量时 swift 会调用它。如果错误仍然存​​在,也许你可以将字段初始化为一些随机值并用 initstruct 修复它们......或者从构造函数调用 initstruct
        • C 库是开源的,但这不允许我编写构造函数(除非可以使用 Swift 扩展,但未尝试过)。
        • 我以为你可以使用 C++,你可以在 C++ 中为 struct 放置构造函数
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多