How convert String to UnsafePointer<Int8>, not Mutable

I'm trying to use Winapi and I need LPCSTR format strings, which is essentially the UnsafePointer<Int8> format
My 2 options are let s = LPCSTR("String") and let s = {(s: LPCSTR) in s}("String") but they both give dangling pointers
The compiler prompts to use the withCString method, but there the pointer is valid only inside the closure, and I need a valid pointer to the entire program (well, or the structure object where it is created)
If you can simply pass a String to a function, this does not work for structure fields outside the constructor, but warnings also appear in the constructor

If your string is fully known at compile time, you can explicitly type it as StaticString, which will give you a stable address as utf8Start. Otherwise you have to either

  • copy bytes out into your own preallocated buffer within a withCString closure;
  • or call the function taking LPCSTR within a withCString closure, making sure that no pointers to that address are held by the time this closure returns;
  • or create your own String-like type that can provide the required memory guarantees you need, possibly making it a reference type or a non-copyable type.

String is a value type and thus it doesn't have a stable address in memory (I'd argue this is by definition, but I'm afraid there are enough people to pick all possible nits with that).

3 Likes

utf8Start give UInt8 but I need Int8
in functions call work fine String
problem only with struct fields

NSString + utf8String might work for you to get the stable address of a c string (you'd need to ensure that NSString object stays alive). The utf8String documentation states "may have a lifetime shorter than the string object", so worth double checking what actually happens in your usage scenario.

Other than that if you have a temporary c string you may strdup it to make your own stable copy (onus will be on you to release the copied memory once you are done).

You can rebind memory it points to to the exact type that you need.

No. Do not do this. That's in no way a correct use of memory rebinding. You can pass an Unsafe[Mutable]Pointer<UInt8> to any C function that expects an Unsafe[Mutable]Pointer<Int8>. Just pass utf8Start directly. This is the exact usecase that prompted SE-0324.

Do I understand correctly that in your opinion documentation for that function is incorrect? Because this is pretty much the use case it covers in the "Discussion" section:

The following example temporarily rebinds the memory of a UInt64 pointer to Int64, then accesses a property on the signed integer.

let uint64Pointer: UnsafePointer<UInt64> = fetchValue()
let isNegative = uint64Pointer.withMemoryRebound(
   to: Int64.self, capacity: 1
) {
   return $0.pointee < 0
}

No, that's fine, although it would be better to just use Int64(truncatingIfNeeded: unit64pointer.pointee) < 0.

The problem is that he's planning on passing the pointer to a C function that may or may not keep it around longer than the call to withMemoryRebound which would be an error. The typed pointer and the rebound pointer must not both be accessed at the same time. Furthermore, the rebind is uneccesary since you can just pass the UInt8 pointer and it will implicitly convert to an Int8 pointer when calling a C function.

We really need a Swift equivalent of the Rustonomicon.

1 Like

As clarified by the OP in one of the posts above this doesn't seem to be a problem, assigning this pointer to a struct property is. IIUC even basic Win32 APIs for registering a window class take a struct value that you customize by assigning pointers to its properties and not by calling C functions:

// Register the window class.
const wchar_t CLASS_NAME[]  = L"Sample Window Class";

WNDCLASS wc = { };

wc.lpfnWndProc   = WindowProc;
wc.hInstance     = hInstance;
wc.lpszClassName = CLASS_NAME;
1 Like

Oh, blech, yeah I forgot how ugly Win32 is. In this case rebinding is still wrong. You definitely don't want to save the rebound pointer in a struct, but I don't remember what the correct way to do the conversion is (if there even is one). I want to say you need to store an UnsafeRawPointer.

The custom string type solution is looking increasingly like the best way to deal with this.

Is 25:52 of WWDC 2020 "Safely Manage Pointers in Swift." a useful reference?

uses .load(fromByteOffset:as) to prevent rebinding of memory for access.