Несовместимые типы указателей, передающие 'struct NSArray *' параметру типа 'NSArray * - PullRequest
1 голос
/ 14 марта 2020

Я следовал учебному пособию по вызову объективного кода c из golang. Учебное пособие находится по этой ссылке

Код выглядит следующим образом (он аналогичен учебному пособию)

main. go

package main

import (
    "fmt"
    "net/url"
    "strconv"
    "unsafe"
)

//#cgo CFLAGS: -x objective-c
//#cgo LDFLAGS: -framework Foundation
//#include "foundation.h"
import "C"

// NSString -> C string
func cstring(s *C.NSString) *C.char { return C.nsstring2cstring(s) }

// NSString -> Go string
func gostring(s *C.NSString) string { return C.GoString(cstring(s)) }

// NSNumber -> Go int
func goint(i *C.NSNumber) int { return int(C.nsnumber2int(i)) }

// NSArray length
func nsarraylen(arr *C.NSArray) uint { return uint(C.nsarraylen(arr)) }

// NSArray item
func nsarrayitem(arr *C.NSArray, i uint) unsafe.Pointer {
    return C.nsarrayitem(arr, C.ulong(i))
}

// NSURL -> Go url.URL
func gourl(nsurlptr *C.NSURL) *url.URL {
    nsurl := *C.nsurldata(nsurlptr)

    userInfo := url.UserPassword(
        gostring(nsurl.user),
        gostring(nsurl.password),
    )

    host := gostring(nsurl.host)

    if nsurl.port != nil {
        port := goint(nsurl.port)
        host = host + ":" + strconv.FormatInt(int64(port), 10)
    }

    return &url.URL{
        Scheme:   gostring(nsurl.scheme),
        User:     userInfo, // username and password information
        Host:     host,     // host or host:port
        Path:     gostring(nsurl.path),
        RawQuery: gostring(nsurl.query),    // encoded query values, without '?'
        Fragment: gostring(nsurl.fragment), // fragment for references, without '#'
    }
}

// NSArray<NSURL> -> Go []url.URL
func gourls(arr *C.NSArray) []url.URL {
    var result []url.URL
    length := nsarraylen(arr)

    for i := uint(0); i < length; i++ {
        nsurl := (*C.NSURL)(nsarrayitem(arr, i))
        u := gourl(nsurl)
        result = append(result, *u)
    }

    return result
}

func UserApplicationSupportDirectories() []url.URL {
    return gourls(C.UserApplicationSupportDirectories())
}

func main() {
    fmt.Printf("%#+v\n", UserApplicationSupportDirectories())
}

Foundation.h

#import <Foundation/Foundation.h>

typedef struct _NSURLdata {
    NSString *scheme;
    NSString *user;
    NSString *password;
    NSString *host;
    NSNumber *port;
    NSString *path;
    NSString *query;
    NSString *fragment;
} NSURLdata;

const char* nsstring2cstring(NSString*);
int nsnumber2int(NSNumber*);
unsigned long nsarraylen(NSArray*);
const void* nsarrayitem(NSArray*, unsigned long);
const NSURLdata* nsurldata(NSURL*);
const NSArray* UserApplicationSupportDirectories();

Foundation.m

#import "foundation.h"

const char*
nsstring2cstring(NSString *s) {
    if (s == NULL) { return NULL; }

    const char *cstr = [s UTF8String];
    return cstr;
}

int
nsnumber2int(NSNumber *i) {
    if (i == NULL) { return 0; }
    return i.intValue;
}

unsigned long
nsarraylen(NSArray *arr) {
    if (arr == NULL) { return 0; }
    return arr.count;
}

const void*
nsarrayitem(NSArray *arr, unsigned long i) {
    if (arr == NULL) { return NULL; }
    return [arr objectAtIndex:i];
}

const NSURLdata*
nsurldata(NSURL *url) {
    NSURLdata *urldata = malloc(sizeof(NSURLdata));
    urldata->scheme = url.scheme;
    urldata->user = url.user;
    urldata->password = url.password;
    urldata->host = url.host;
    urldata->port = url.port;
    urldata->path = url.path;
    urldata->query = url.query;
    urldata->fragment = url.fragment;
    return urldata;
}

const NSArray*
UserApplicationSupportDirectories() {
    NSFileManager *manager = [NSFileManager defaultManager];
    return [manager URLsForDirectory: NSApplicationSupportDirectory
                           inDomains: NSUserDomainMask];
}

Когда я создаю этот код, я получаю следующие предупреждения от компилятора

cgo-gcc-prolog:70:47: warning: incompatible pointer types passing 'struct NSArray *' to parameter of type 'NSArray *' [-Wincompatible-pointer-types]
./foundation.h:17:33: note: passing argument to parameter here
cgo-gcc-prolog:88:22: warning: incompatible pointer types passing 'struct NSArray *' to parameter of type 'NSArray *' [-Wincompatible-pointer-types]
./foundation.h:16:34: note: passing argument to parameter here
cgo-gcc-prolog:107:24: warning: incompatible pointer types passing 'struct NSNumber *' to parameter of type 'NSNumber *' [-Wincompatible-pointer-types]
./foundation.h:15:27: note: passing argument to parameter here
cgo-gcc-prolog:125:52: warning: incompatible pointer types passing 'struct NSString *' to parameter of type 'NSString *' [-Wincompatible-pointer-types]
./foundation.h:14:39: note: passing argument to parameter here
cgo-gcc-prolog:143:45: warning: incompatible pointer types passing 'struct NSURL *' to parameter of type 'NSURL *' [-Wincompatible-pointer-types]
./foundation.h:18:34: note: passing argument to parameter here

Индивидуально Компиляция кода Objective- C в XCode не показывает никаких предупреждений, и я чувствую, что CGO -Calls по какой-то причине запутались. Как мне избежать предупреждений здесь?

1 Ответ

1 голос
/ 17 марта 2020

C.NSString, сгенерированный CGO, несовместим с NSString Objective- C. Чтобы избежать предупреждающего сообщения от компилятора, например, в const char* nsstring2cstring(), NSString должно быть передано как void* в параметре в вашей функции Objective- C code (foundation.m foundation.h), и привести ваши void* к NSString и вернуть как C const char*:

const char* nsstring2cstring(void* s) {
    if (s == NULL) { return NULL; }
    NSString *cs = *((__unsafe_unretained NSString **)(s));
    const char *cstr = [cs UTF8String];
    return cstr;
}

В main.go Go функции кода, необходимо передать C.NSString как unsafe.Pointer в nsstring2cstring(void*)

// NSString -> C string
func cstring(s *C.NSString) *C.char { return C.nsstring2cstring(unsafe.Pointer(s)) }
...