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