Com Swift 6.3 ou posterior, marque uma função global com @c, gere o cabeçalho de compatibilidade, compile o chamador como C e faça a vinculação com swiftc. Este passo a passo cria um pequeno programa nativo de linha de comando para Mac que demonstra cada etapa.
A tarefa do leitor é específica: chamar um cálculo em Swift a partir de um arquivo .c comum, sem compilá-lo como Objective-C ou C++. A documentação oficial do lançamento do Swift identifica @c como o recurso de interoperabilidade com C compatível, introduzido no Swift 6.3. Confira a versão do compilador instalado antes de seguir conselhos antigos sobre atributos experimentais. Swift 6.3 lançado
O que torna uma função Swift chamável a partir de C?
Três elementos precisam estar de acordo: a declaração lida pelo compilador C, a convenção de chamada usada pela implementação e os arquivos-objeto fornecidos ao vinculador. Um cabeçalho resolve o primeiro elemento; ele não pode fornecer uma implementação ausente nem alterar a ABI de uma função existente.
O atributo @c dá a uma função global Swift a convenção de chamada de C e restringe sua assinatura a tipos representáveis em C. Um argumento opcional do atributo seleciona seu nome em C. O Swift emite a declaração correspondente na parte compatível com C do cabeçalho de compatibilidade gerado. Esses comportamentos estão especificados na proposta implementada do Swift Evolution. Funções e enumerações compatíveis com C
Neste exemplo, a interface aceita e retorna um inteiro C. O cálculo demonstrativo limita uma porcentagem de exibição ao intervalo inclusivo de zero a 100. Não há buffers, resultados alocados ou tempos de vida de objetos a coordenar. Isso mantém a primeira compilação focada na compilação e na vinculação.
Primeiro, confira a cadeia de ferramentas do Mac
Você precisa de um ambiente de desenvolvimento macOS com Clang, um SDK do macOS e Swift 6.3 ou posterior. A Apple fornece ferramentas de linha de comando com o Xcode e também oferece um pacote separado de Command Line Tools. Se você já tem o Xcode, a Apple informa que não é necessário instalar as ferramentas separadamente. Instalar as ferramentas de linha de comando
Execute estas verificações no Terminal:
xcode-select --print-path
xcrun --sdk macosx swiftc --version
xcrun --sdk macosx clang --version
Confira a versão do próprio compilador Swift. Um compilador antigo não passa a oferecer o recurso @c só porque um SDK mais recente está instalado. Se houver várias versões do Xcode, a Apple documenta como selecionar a versão ativa em Xcode > Settings > Locations > Command Line Tools ou substituí-la para uma sessão do shell com DEVELOPER_DIR. Configurar os ajustes das ferramentas de linha de comando
Os comandos abaixo usam xcrun para invocar as ferramentas de desenvolvimento selecionadas. A Apple documenta essa função de localizar ferramentas em sua referência de compilação pela linha de comando. Perguntas frequentes sobre compilação pela linha de comando com o Xcode
Crie um diretório de trabalho vazio e salve nele os dois arquivos-fonte abaixo. Execute os comandos de compilação nesse diretório. Esta é uma compilação nativa para o Mac local; ela não cria um binário universal nem configura outra plataforma.
Escreva a implementação Swift e o chamador em C
Defina a função Swift exportada
Salve este arquivo-fonte Swift completo como Percent.swift:
@c(demo_clamp_percent)
public func clampPercent(_ value: CInt) -> CInt {
if value < 0 {
return 0
}
if value > 100 {
return 100
}
return value
}
O nome Swift é clampPercent; o nome visível para C é demo_clamp_percent. CInt corresponde ao int de C. Usar esse pareamento explícito evita presumir que o Int do Swift significa int em C: o tipo documentado correspondente ao Int do Swift é ptrdiff_t. Funções e enumerações compatíveis com C
A função compara valores e retorna uma entrada ou uma constante. Seu cálculo não causa estouro aritmético, inclusive para entradas fora do intervalo de porcentagem. O prefixo do nome exportado é um exemplo de convenção para distinguir essa interface de outras funções em um programa maior.
Chame a função de um programa C completo
Salve este arquivo como main.c. A compilação criará o cabeçalho incluído:
#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;
}
As três entradas exercitam o limite inferior, um valor inalterado e o limite superior. O programa verifica os resultados explicitamente e retorna um status diferente de zero em caso de divergência, tornando sua saída mais útil do que uma demonstração que apenas imprime um número.
Gere o cabeçalho, compile o C e vincule o programa
Execute esta sequência no shell, a partir do diretório que contém os dois arquivos. Cada etapa interrompe a sequência se falhar:
(
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
)
O primeiro comando produz o objeto Swift e o cabeçalho de compatibilidade. O driver do Swift define -emit-object como a saída de arquivo-objeto e -parse-as-library como a análise do código-fonte de uma biblioteca, em vez de um script. Assim, o ponto de entrada do executável fica no arquivo C. Opções do driver do compilador Swift
Apesar do nome histórico, -emit-objc-header-path também solicita a seção C do cabeçalho de compatibilidade para as declarações @c. A proposta implementada preserva explicitamente esse sinalizador para esse fim. Não substitua manualmente o cabeçalho gerado por um protótipo presumido. Funções e enumerações compatíveis com C
O segundo comando é a verificação decisiva da linguagem-fonte. A documentação do Clang descreve -x como a opção que seleciona a linguagem de entrada e -c como a que produz um arquivo-objeto sem vincular. Aqui, -x c -std=c11 faz o chamador ser compilado como C11 enquanto lê o cabeçalho realmente gerado. Guia de comandos do Clang
A invocação final do compilador vincula os dois arquivos-objeto usando o driver do Swift. O guia do próprio compilador Swift recomenda pedir que swiftc vincule os arquivos, em vez de montar manualmente um comando de vinculação de nível inferior. Depuração do compilador
A saída esperada, com base nas ramificações do exemplo, é:
-12 -> 0
42 -> 42
140 -> 100
Estes comandos foram conferidos com base na documentação consultada, mas não foram executados aqui: o ambiente de pesquisa é Linux e não tem swiftc nem Clang disponíveis. A saída acima é um resultado esperado, não o relato de uma execução de teste em Mac.
Escolha a interface adequada ao seu código existente
Para uma interface pequena e nova com Swift 6.3 ou posterior, a abordagem com cabeçalho gerado mantém a declaração diretamente vinculada à implementação. A documentação do lançamento também oferece suporte a uma segunda direção: @c @implementation implementa uma função já declarada em um cabeçalho C importado, e o Swift verifica se as assinaturas correspondem. Isso é útil quando uma API C existente precisa continuar sendo o contrato da interface. Swift 6.3 lançado
Se o projeto ao redor já chama Swift por meio de C++, as vinculações C++ geradas pelo Swift oferecem outra alternativa. As funções públicas do Swift são representadas por funções C++ inline dentro de um namespace de módulo. Um chamador em C precisaria de um adaptador separado, compilado como C++, que exponha uma pequena declaração visível para C. A unidade de tradução C incluiria o cabeçalho C desse adaptador. Misturar Swift e C++
Nesse adaptador, extern \"C\" especifica a vinculação à linguagem C na declaração ou definição em C++. Isso não converte o conteúdo de um cabeçalho C++ para a sintaxe de C. O rascunho de trabalho do C++ define as especificações de vinculação e distingue a vinculação à linguagem das declarações comuns. Rascunho de trabalho do C++: especificações de vinculação
Para este tutorial, atualizar um compilador antigo para uma versão compatível com @c é o caminho direto e compatível. A proposta descreve @_cdecl como seu predecessor experimental; copiar essa grafia antiga em um exemplo novo esconderia um limite importante entre versões. Funções e enumerações compatíveis com C
Diagnostique as falhas no ponto em que ocorrem
Use a primeira etapa de compilação que falhar para restringir a investigação. Uma falha ao gerar o cabeçalho, um erro do analisador C e um símbolo não resolvido exigem correções diferentes.
| Primeira falha | Próxima verificação |
|---|---|
O Swift rejeita @c |
Confira novamente a versão selecionada do compilador Swift; este exemplo requer Swift 6.3 ou posterior. |
O C não consegue encontrar Percent-Swift.h |
Confirme que o comando do Swift foi concluído com sucesso e que os dois arquivos estão sendo compilados a partir do diretório correto. |
O C não consegue encontrar demo_clamp_percent |
Examine o cabeçalho recém-gerado e confira a grafia do atributo; recompile após alterações no código-fonte. |
| O C acusa sintaxe de C++ ou Objective-C | Verifique qual cabeçalho foi incluído e qual cadeia de ferramentas o gerou. Mantenha a compilação explicitamente como C durante a investigação. |
| A vinculação não consegue resolver a função exportada | Confirme que Percent.o está incluído no comando final e foi recompilado a partir do código-fonte Swift atual. |
| O executável informa resultados inesperados | Leia o par impresso de entrada e resultado e compare-o com as ramificações do Swift. |
Esta sequência acompanha as etapas do compilador documentadas pelo Clang: a análise sintática determina se o código-fonte é válido, a geração de objetos produz o código compilado e a vinculação combina as entradas compiladas. Portanto, uma compilação C bem-sucedida verifica apenas parte da tarefa. Guia de comandos do Clang
Ao adaptar o exemplo, mantenha pequena a função visível para C e defina um contrato explícito para cada novo parâmetro. Para texto, decida como serão tratados a codificação e o comprimento; para buffers, decida a capacidade e a propriedade antes de escrever o wrapper. Essas são decisões de projeto de interface que vão além desta demonstração restrita a inteiros.
A verificação local concluída tem três resultados observáveis: o cabeçalho gerado é aceito durante a compilação explicitamente como C, as duas implementações são vinculadas em um único executável e o chamador verifica as entradas escolhidas. Preserve essas verificações ao substituir o cálculo demonstrativo pela sua própria função Swift.
Fontes
- Swift 6.3 lançado — Swift.org
- Funções e enumerações compatíveis com C — Swift Evolution
- Instalar as ferramentas de linha de comando — Apple
- Configurar os ajustes das ferramentas de linha de comando — Apple
- Perguntas frequentes sobre compilação pela linha de comando com o Xcode — Apple
- Opções do driver do compilador Swift — Swift Project
- Guia de comandos do Clang — LLVM Project
- Depuração do compilador — Swift Project
- Misturar Swift e C++ — Swift.org
- Rascunho de trabalho do C++: especificações de vinculação — C++ Working Draft