Search code examples
swiftpointersswift3castingunsafe-pointers

Assigning UnsafeMutablePointer value to UnsafePointer without unsafeBitCast


I am working with a C API that defines a structure with const char* and a function that returns a char* and am trying to find the best way to do the assignment.

Is there a way to do this without using unsafeBitCast? If I don't put the cast then I get this error:

Cannot assign value of type 'UnsafeMutablePointer<pchar>' 
(aka 'UnsafeMutablePointer<UInt8>') 
to type 'UnsafePointer<pchar>!' 
(aka 'ImplicitlyUnwrappedOptional<UnsafePointer<UInt8>>')

Also, will the initialization of pairPtr below using pair() allocate a pair struct on the stack to initialize the allocated pair on the heap because this seems inefficient for the case where the structure just has to be zero'd out.

Here is sample code:

C library header (minimized to demonstrate the problem):

#ifndef __PAIR_INCLUDE__
#define __PAIR_INCLUDE__

typedef unsigned char pchar;

pchar*
pstrdup(const pchar* str);

typedef struct _pair {
    const pchar* left;
    const pchar* right;
} pair;

#endif // __PAIR_INCLUDE__

My Swift code:

import pair

let leftVal = pstrdup("left")
let rightVal = pstrdup("right")

let pairPtr = UnsafeMutablePointer<pair>.allocate(capacity: 1)
pairPtr.initialize(to: pair())

// Seems like there should be a better way to handle this:
pairPtr.pointee.left = unsafeBitCast(leftVal, to: UnsafePointer<pchar>.self)
pairPtr.pointee.right = unsafeBitCast(rightVal, to: UnsafePointer<pchar>.self)

The C code:

#include "pair.h"
#include <string.h>

pchar*
pstrdup(const pchar* str) {
    return strdup(str);
}

The module definition:

module pair [extern_c] {
    header "pair.h"
    export *
}

Solution

  • You can create an UnsafePointer<T> from an UnsafeMutablePtr<T> simply with

    let ptr = UnsafePointer(mptr)
    

    using the

    /// Creates an immutable typed pointer referencing the same memory as the
    /// given mutable pointer.
    ///
    /// - Parameter other: The pointer to convert.
    public init(_ other: UnsafeMutablePointer<Pointee>)
    

    initializer of UnsafePointer. In your case that would for example be

    p.left = UnsafePointer(leftVal)