Con Swift 6.3 o versioni successive, contrassegna una funzione globale con @c, genera il suo header di compatibilità, compila il chiamante come C e collega il tutto con swiftc. Questa guida realizza un piccolo programma nativo da riga di comando per Mac che mostra ciascun passaggio.
L’obiettivo per chi legge è specifico: chiamare un calcolo Swift da un normale file .c senza compilarlo come Objective-C o C++. La documentazione ufficiale della versione di Swift indica @c come la funzionalità di interoperabilità con C introdotta in Swift 6.3. Verifica la versione del compilatore installato prima di seguire i vecchi consigli sugli attributi sperimentali. Swift 6.3 Released
Cosa rende una funzione Swift chiamabile da C?
Tre elementi devono essere coerenti: la dichiarazione letta dal compilatore C, la convenzione di chiamata usata dall’implementazione e i file oggetto forniti al linker. Un header risolve il primo punto; non può fornire un’implementazione mancante né modificare l’ABI di una funzione esistente.
L’attributo @c assegna a una funzione globale Swift la convenzione di chiamata C e limita la firma ai tipi rappresentabili in C. Un argomento facoltativo dell’attributo seleziona il nome C. Swift genera la dichiarazione corrispondente nella sezione compatibile con C dell’header di compatibilità generato. Questi comportamenti sono specificati nella proposta Swift Evolution implementata. C compatible functions and enums
In questo esempio, l’interfaccia accetta e restituisce un intero C. Il calcolo dimostrativo limita una percentuale da visualizzare all’intervallo inclusivo da zero a 100. Non ci sono buffer, risultati allocati o durate di vita degli oggetti da coordinare. In questo modo, la prima compilazione si concentra su compilazione e collegamento.
Verifica prima la toolchain Mac
Servono un ambiente di sviluppo macOS con Clang, un SDK macOS e Swift 6.3 o versioni successive. Apple fornisce gli strumenti da riga di comando con Xcode e offre anche un pacchetto separato di Command Line Tools. Se hai già Xcode, Apple afferma che non occorre installare separatamente gli strumenti. Installing the command-line tools
Esegui questi controlli in Terminale:
xcode-select --print-path
xcrun --sdk macosx swiftc --version
xcrun --sdk macosx clang --version
Controlla la versione del compilatore Swift stesso. Un compilatore più vecchio non acquisisce la funzionalità @c solo perché è installato un SDK più recente. Se sono presenti più versioni di Xcode, Apple documenta come selezionare quella attiva in Xcode > Settings > Locations > Command Line Tools oppure come impostare una versione diversa per una sessione della shell con DEVELOPER_DIR. Configuring command-line tools settings
I comandi seguenti usano xcrun per richiamare gli strumenti di sviluppo selezionati. Apple documenta questo ruolo di individuazione degli strumenti nel riferimento alla compilazione da riga di comando. Building from the Command Line with Xcode FAQ
Crea una directory di lavoro vuota e salva al suo interno entrambi i file sorgente riportati sotto. Esegui i comandi di compilazione da quella directory. Questa è una compilazione nativa per il Mac locale; non crea un binario universale né configura un’altra piattaforma.
Scrivi l’implementazione Swift e il chiamante C
Definisci la funzione Swift esportata
Salva questo file sorgente Swift completo come Percent.swift:
@c(demo_clamp_percent)
public func clampPercent(_ value: CInt) -> CInt {
if value < 0 {
return 0
}
if value > 100 {
return 100
}
return value
}
Il nome Swift è clampPercent; il nome esposto a C è demo_clamp_percent. CInt corrisponde al tipo C int. Questa corrispondenza esplicita evita di presumere che Swift Int significhi C int: la corrispondenza documentata per Swift Int è ptrdiff_t. C compatible functions and enums
La funzione confronta i valori e restituisce un input oppure una costante. Il calcolo non comporta overflow aritmetico, nemmeno per input fuori dall’intervallo della percentuale. Il prefisso del nome esportato è una convenzione d’esempio per distinguere questa interfaccia dalle altre funzioni di un programma più ampio.
Chiamala da un programma C completo
Salva questo file come main.c. La compilazione creerà l’header incluso:
#include <stddef.h>
#include <stdio.h>
#include "Percent-Swift.h"
int main(void) {
const int inputs[] = {-12, 42, 140};
const int expected[] = {0, 42, 100};
const size_t count = sizeof inputs / sizeof inputs[0];
for (size_t i = 0; i < count; ++i) {
const int actual = demo_clamp_percent(inputs[i]);
printf("%d -> %d\n", inputs[i], actual);
if (actual != expected[i]) {
fprintf(stderr, "Unexpected result at index %zu\n", i);
return 1;
}
}
return 0;
}
I tre input coprono il limite inferiore, un valore invariato e il limite superiore. Il programma verifica esplicitamente i risultati e restituisce uno stato diverso da zero in caso di discrepanza, perciò il suo output è più utile di una dimostrazione che si limita a stampare un numero.
Genera l’header, compila il C e collega il programma
Esegui questa sequenza in una shell, dalla directory che contiene i due file. Ogni passaggio interrompe la sequenza se non riesce:
(
set -eu
SDK_PATH="$(xcrun --sdk macosx --show-sdk-path)"
xcrun --sdk macosx swiftc \
-sdk "$SDK_PATH" \
-parse-as-library \
-module-name Percent \
-emit-object Percent.swift \
-emit-objc-header-path Percent-Swift.h \
-o Percent.o
xcrun --sdk macosx clang \
-isysroot "$SDK_PATH" \
-x c -std=c11 \
-Wall -Wextra -Werror \
-c main.c -o main.o
xcrun --sdk macosx swiftc \
-sdk "$SDK_PATH" \
main.o Percent.o \
-o percent-demo
./percent-demo
)
Il primo comando produce l’oggetto Swift e l’header di compatibilità. Il driver Swift definisce -emit-object come output di file oggetto e -parse-as-library come analisi del sorgente come libreria anziché come script. In questo modo, il punto d’ingresso dell’eseguibile rimane nel file C. Swift compiler driver options
Nonostante il nome storico, -emit-objc-header-path richiede anche la sezione C dell’header di compatibilità per le dichiarazioni @c. La proposta implementata conserva esplicitamente questo flag per tale scopo. Non sostituire manualmente l’header generato con un prototipo presunto. C compatible functions and enums
Il secondo comando è la verifica decisiva del linguaggio sorgente. Clang documenta -x come opzione per selezionare il linguaggio di input e -c come opzione per produrre un file oggetto senza collegarlo. Qui, -x c -std=c11 fa compilare il chiamante come C11 usando l’header effettivamente generato. Clang command guide
L’invocazione finale del compilatore collega entrambi i file oggetto con il driver Swift. La guida del compilatore Swift consiglia di chiedere a swiftc di collegare i file invece di assemblare manualmente un comando di collegamento di livello inferiore. Debugging the Compiler
L’output previsto, ricavato dai rami dell’esempio, è:
-12 -> 0
42 -> 42
140 -> 100
Questi comandi sono stati confrontati con la documentazione consultata, ma qui non sono stati eseguiti: l’ambiente di ricerca è Linux e non dispone né di swiftc né di Clang. L’output sopra è il risultato previsto, non l’esito dichiarato di una prova su Mac.
Scegli l’interfaccia adatta al codice esistente
Per una nuova interfaccia piccola con Swift 6.3 o versioni successive, l’approccio con l’header generato mantiene la dichiarazione direttamente collegata all’implementazione. La documentazione della versione supporta anche una seconda direzione: @c @implementation implementa una funzione già dichiarata in un header C importato e Swift verifica che le firme corrispondano. È utile quando un’API C esistente deve restare il contratto dell’interfaccia. Swift 6.3 Released
Se il progetto circostante chiama già Swift tramite C++, i binding C++ generati da Swift offrono un’altra possibilità. Le funzioni Swift pubbliche vengono rappresentate come funzioni C++ inline all’interno di uno spazio dei nomi del modulo. Un chiamante C avrebbe bisogno di un adattatore separato compilato come C++, che esponga una piccola dichiarazione utilizzabile da C. L’unità di traduzione C includerebbe l’header C dell’adattatore. Mixing Swift and C++
In un adattatore di questo tipo, extern "C" specifica il collegamento al linguaggio C per la dichiarazione o definizione C++. Non converte il contenuto di un header C++ in sintassi C. La bozza di lavoro C++ definisce le specifiche di collegamento e distingue il collegamento al linguaggio dalle dichiarazioni ordinarie. C++ working draft: Linkage specifications
Per questo tutorial, aggiornare un compilatore più vecchio a uno che supporta @c è la via diretta supportata. La proposta descrive @_cdecl come suo predecessore sperimentale; copiare questa vecchia sintassi in un nuovo esempio nasconderebbe un’importante differenza di versione. C compatible functions and enums
Diagnostica i problemi nel passaggio in cui si verificano
Usa il primo passaggio di compilazione che fallisce per restringere l’indagine. Un errore nella generazione dell’header, un errore del parser C e un simbolo non risolto richiedono correzioni diverse.
| Primo errore | Controllo successivo |
|---|---|
Swift rifiuta @c |
Ricontrolla la versione del compilatore Swift selezionato; questo esempio richiede Swift 6.3 o versioni successive. |
C non trova Percent-Swift.h |
Verifica che il comando Swift sia riuscito e che entrambi i file siano compilati dalla directory prevista. |
C non trova demo_clamp_percent |
Esamina l’header appena generato e controlla la grafia dell’attributo; ricompila dopo aver modificato il sorgente. |
| C segnala sintassi C++ o Objective-C | Verifica quale header è stato incluso e quale toolchain lo ha generato. Durante l’indagine, mantieni esplicita la compilazione C. |
| Il collegamento non risolve la funzione esportata | Verifica che Percent.o sia incluso nel comando finale e sia stato ricompilato dal sorgente Swift corrente. |
| L’eseguibile segnala risultati inattesi | Leggi la coppia input/risultato stampata e confrontala con i rami Swift. |
Questa sequenza segue le fasi del compilatore documentate da Clang: l’analisi sintattica determina se il sorgente è valido, la generazione dell’oggetto produce codice compilato e il collegamento combina gli input compilati. Una compilazione C riuscita, quindi, verifica solo una parte dell’obiettivo. Clang command guide
Quando adatti l’esempio, mantieni piccola la funzione esposta a C e definisci un contratto esplicito per ogni nuovo parametro. Per il testo, stabilisci la codifica e la gestione della lunghezza; per i buffer, definisci capacità e proprietà prima di scrivere il wrapper. Sono decisioni di progettazione dell’interfaccia che vanno oltre questa dimostrazione basata solo su interi.
Il controllo locale completato produce tre risultati osservabili: l’header generato viene accettato durante la compilazione esplicita come C, entrambe le implementazioni vengono collegate in un unico eseguibile e il chiamante verifica gli input scelti. Mantieni questi controlli quando sostituisci il calcolo dimostrativo con la tua funzione Swift.
Fonti
- Swift 6.3 Released — Swift.org
- C compatible functions and enums — Swift Evolution
- Installing the command-line tools — Apple
- Configuring command-line tools settings — Apple
- Building from the Command Line with Xcode FAQ — Apple
- Swift compiler driver options — Swift Project
- Clang command guide — LLVM Project
- Debugging the Compiler — Swift Project
- Mixing Swift and C++ — Swift.org
- C++ working draft: Linkage specifications — C++ Working Draft