Is there a mismatch between String.utf8CString and imported C declarations?

When one wants to call C APIs that use C strings, it looks natural to use String.utf8CString, which returns a "contiguously stored null-terminated UTF-8 representation of the string".

String.utf8CString returns ContiguousArray<CChar>. CChar is documented to be signed or unsigned, depending on the platform:

This will be the same as either CSignedChar (in the common case) or CUnsignedChar, depending on the platform.

However, C apis that use strings are imported into Swift as consuming Int8. For example, the following function is imported as (UnsafePointer<Int8>?) -> Int32:

int cFunc(const char *inStr);

It appears that we have a mismatch. A platform-dependent CChar on one side, and a constant Int8 on the other side. But maybe this Int8 is just a CChar in disguise? Maybe it could be imported as UInt8 on other platforms? In this case, there would not be any mismatch, but just the illusion of a mismatch. I don't know.

Which one of the versions below is platform-independent?

// Version 1
string.utf8CString.withUnsafeBufferPointer { buffer in // CChar
    buffer.withMemoryRebound(to: Int8.self) { buffer in // Int8
        cFunc(buffer.baseAddress!)
    }
}

// Version 2
string.utf8CString.withUnsafeBufferPointer { buffer in // CChar
    cFunc(buffer.baseAddress!)
}

Yes, this is correct. Maybe we should change the importer to preserve "CChar" even though we don't preserve any of the other C types (CInt, CUnsignedLong), just because of the weirdness around signed/unsigned char.

4 Likes

SR-466: Imported APIs taking chars should use CChar

3 Likes

Thanks Jordan and Ben, both for removing any shadow of a doubt, and for wondering if something could be improved here. I added to SR-466 a link to this thread. Have a nice day :-)

I looked into how much work this would be and it's not a lot! So I tagged it as a starter bug.

1 Like