C++ en macOS: cómo compilar código nativo y qué lenguaje elegir en Apple

macOS incluye prácticamente todo lo necesario para convertir un Mac en una estación de desarrollo C++ sin instalar un entorno GNU completo. Apple utiliza Clang y LLVM como parte de su toolchain oficial, con soporte para C, C++, Objective-C y Objective-C++, mientras Swift ocupa actualmente el lugar preferente para desarrollar nuevas aplicaciones destinadas a las plataformas de la compañía. Para programadores y administradores de sistemas, conocer estas herramientas permite ir bastante más allá de compilar un sencillo hello.cpp: también ayuda a entender arquitecturas ARM64, SDK, bibliotecas, binarios universales, linking y compatibilidad entre versiones de macOS.

Las claves de la programación nativa en macOS en 30 segundos

  • Apple incluye Clang, LLVM y libc++ dentro de sus herramientas de desarrollo para compilar C y C++.
  • Swift es la opción preferente de Apple para nuevas aplicaciones de macOS, iPhone, iPad, Watch y Vision Pro.
  • Objective-C sigue siendo importante para mantener software y bibliotecas históricas del ecosistema Cocoa.
  • C y C++ encajan especialmente bien en software de sistemas, herramientas CLI, motores, bibliotecas y código multiplataforma.
  • Metal Shading Language está destinado a programación de GPU sobre Metal.

Para un administrador acostumbrado a Linux, el cambio no suele estar en C++ sino en todo lo que rodea al compilador. En lugar de GCC, glibc, ELF y herramientas GNU, macOS gira alrededor de Apple Clang, libc++, los SDK de Apple, Mach-O, xcrun, LLDB y frameworks del sistema.

La llegada de Apple Silicon añade otra variable. Los Mac actuales utilizan ARM64, mientras todavía existen aplicaciones, bibliotecas y servidores de compilación basados en x86-64. Saber qué arquitectura contiene un ejecutable y contra qué versión de macOS se ha construido puede evitar bastantes horas persiguiendo errores que, aparentemente, parecen problemas del código fuente.

Preparar macOS para compilar C y C++

Para trabajar desde Terminal no es obligatorio instalar todo Xcode. Apple ofrece sus Command Line Tools, que proporcionan los componentes básicos necesarios para desarrollar y compilar software.

La instalación puede iniciarse con:

xcode-select --install

Una vez terminada, conviene comprobar qué directorio de desarrollo tiene activo el sistema:

xcode-select -p

Cuando únicamente están instaladas las Command Line Tools suele aparecer:

/Library/Developer/CommandLineTools

El siguiente paso es comprobar Clang:

clang++ --version

En un Mac Apple Silicon debería aparecer un objetivo parecido a:

Target: arm64-apple-darwin...Lenguaje del código: HTTP (http)

En un Mac Intel será normalmente:

Target: x86_64-apple-darwin...Lenguaje del código: HTTP (http)

Hay otra forma especialmente útil de encontrar las herramientas dentro del entorno de Apple:

xcrun --find clang++

Y para saber qué SDK de macOS está utilizando el toolchain:

xcrun --show-sdk-path

xcrun merece incorporarse al repertorio de cualquier administrador de Mac que compile software habitualmente. En máquinas con varias versiones de Xcode, SDK diferentes o toolchains alternativos ayuda a resolver qué herramienta se está utilizando realmente.

Apple documenta swift, clang y LLVM como los compiladores disponibles en sus herramientas de desarrollo para construir código Swift, C, C++, Objective-C y Objective-C++.

Compilar un primer programa C++

Un ejemplo mínimo puede guardarse como hello.cpp:

#include <iostream>
#include <string>

int main()
{
    std::string name;

    std::cout << "Name: ";
    std::getline(std::cin, name);

    std::cout << "Hello, " << name << "!\n";

    return 0;
}Lenguaje del código: PHP (php)

Para compilarlo:

clang++ hello.cpp -o helloLenguaje del código: CSS (css)

Y ejecutarlo:

./hello

Para un proyecto moderno conviene además indicar expresamente el estándar:

clang++ -std=c++20 hello.cpp -o hello

O, cuando el código lo requiera:

clang++ -std=c++23 hello.cpp -o hello

Apple mantiene su propia tabla de compatibilidad C++ para Apple Clang y libc++. Esto es importante porque poder seleccionar -std=c++23 o trabajar con una versión reciente de Clang no implica necesariamente que cada característica de ese estándar esté disponible en cualquier versión de Xcode o macOS.

Para desarrollo merece la pena activar avisos:

clang++ -std=c++20 \
  -Wall \
  -Wextra \
  -Wpedantic \
  hello.cpp \
  -o hello

Y para depurar:

clang++ -std=c++20 -Wall -Wextra -g hello.cpp -o hello

macOS incluye LLDB:

lldb ./hello

Una vez dentro:

breakpoint set --name main
runLenguaje del código: JavaScript (javascript)

No hace falta instalar GDB para disponer de un debugger nativo bien integrado con el toolchain de Apple.

Apple Silicon, Mach-O y binarios universales

En administración de sistemas macOS, compilar correctamente es solo una parte del problema. También interesa saber qué se ha compilado.

La arquitectura puede consultarse con:

uname -m

En Apple Silicon:

arm64

Para examinar el ejecutable:

file ./hello

Una compilación nativa habitual mostrará algo parecido a:

Mach-O 64-bit executable arm64

Aquí aparece una de las diferencias con Linux. Los ejecutables de macOS utilizan el formato Mach-O, no ELF.

También puede utilizarse:

otool -L ./hello

Este comando muestra las bibliotecas dinámicas de las que depende el programa.

En un ejecutable C++ pueden aparecer componentes del sistema como libc++.

Para examinar información adicional:

otool -hv ./hello

Estas herramientas resultan especialmente útiles cuando un programa funciona en el Mac del desarrollador pero falla en otra máquina.

Forzar una arquitectura

Clang permite indicar explícitamente ARM64:

clang++ -arch arm64 hello.cpp -o hello-arm64Lenguaje del código: CSS (css)

También puede solicitarse x86-64:

clang++ -arch x86_64 hello.cpp -o hello-x86_64Lenguaje del código: CSS (css)

Pero compilar para una arquitectura distinta no resuelve automáticamente las dependencias. Todas las bibliotecas utilizadas también deben estar disponibles para el objetivo correspondiente.

Un problema típico es:

Undefined symbols for architecture arm64

No significa necesariamente que exista un error en C++. Puede indicar que el linker está recibiendo una biblioteca x86-64 mientras intenta construir un ejecutable ARM64.

Para comprobar una biblioteca:

file libexample.dylibLenguaje del código: CSS (css)

Crear un Universal Binary

macOS permite incluir código para varias arquitecturas dentro de un mismo binario.

Puede compilarse primero cada versión:

clang++ -arch arm64 hello.cpp -o hello-arm64
clang++ -arch x86_64 hello.cpp -o hello-x86_64Lenguaje del código: CSS (css)

Después pueden combinarse:

lipo -create \
  hello-arm64 \
  hello-x86_64 \
  -output hello-universal

Y comprobarse:

lipo -info hello-universal

Un binario de este tipo puede contener simultáneamente versiones ARM64 y x86-64.

Para herramientas internas que solo van a ejecutarse en los Mac Apple Silicon de una organización normalmente no resulta necesario. Para distribuir software entre equipos heterogéneos puede ser mucho más interesante.

Saber para qué macOS se está compilando

Otra variable importante es el deployment target.

Puede indicarse al compilador:

clang++ -std=c++20 \
  -mmacosx-version-min=14.0 \
  hello.cpp \
  -o hello

Esto no transforma automáticamente cualquier uso de APIs recientes en compatible con macOS 14, pero participa en la definición del objetivo mínimo del binario.

El punto importa especialmente cuando se compila en un Mac muy reciente para distribuir posteriormente el resultado a servidores o estaciones con versiones anteriores de macOS.

Los mejores lenguajes nativos para desarrollar en Apple

Hablar del «mejor lenguaje para Apple» sin especificar el trabajo que se quiere realizar conduce fácilmente a recomendaciones poco útiles.

Apple permite trabajar con varios lenguajes dentro de su toolchain y Xcode. La propia documentación cita Swift, Objective-C, C y C++, y la elección cambia según se desarrolle una aplicación gráfica, una herramienta de sistema, un motor multiplataforma o código para GPU. Apple presenta Swift como su lenguaje preferente para proyectos nuevos.

LenguajeMejor encaje en AppleSituación actual
SwiftNuevas apps macOS, iOS, iPadOS, watchOS y visionOS, CLI y serviciosOpción preferente para proyectos nuevos
Objective-CCódigo Cocoa existente, frameworks antiguos, interoperabilidadMuy relevante para mantenimiento
C++Motores, aplicaciones multiplataforma, HPC, bibliotecas y código de rendimientoSoporte oficial con Apple Clang
CSistemas, bibliotecas de bajo nivel, POSIX, interoperabilidadSigue siendo básico en el stack
Objective-C++Integrar Cocoa/Objective-C con C++Muy útil como lenguaje puente
Metal Shading LanguageShaders, gráficos y computación sobre GPULenguaje especializado de Metal

1. Swift: la primera opción para una aplicación Apple nueva

Si el proyecto nace específicamente para macOS, iOS o cualquiera de las plataformas modernas de Apple, Swift es normalmente el punto de partida más lógico.

Apple lo describe como su lenguaje preferente para escribir nuevo software en sus plataformas. Swift dispone de gestión de memoria, seguridad de tipos, optionals, concurrencia moderna y acceso directo a frameworks como SwiftUI, Foundation o AppKit.

Un programa mínimo puede escribirse como:

print("Hello from Swift")Lenguaje del código: PHP (php)

Y ejecutarse directamente:

swift hello.swiftLenguaje del código: CSS (css)

O compilarse:

swiftc hello.swift -o hello
./hello

Swift tampoco está limitado a interfaces de iPhone. El proyecto se presenta actualmente como un lenguaje generalista y multiplataforma que puede utilizarse para herramientas CLI, servicios, software embebido y otros escenarios además del desarrollo de aplicaciones Apple.

Para herramientas internas de administración de macOS resulta una opción que a veces se pasa por alto.

Un administrador puede crear un ejecutable nativo sin depender de que Python, Node.js o Ruby estén instalados en la máquina de destino.

2. Objective-C: menos habitual para empezar, todavía importante

Objective-C fue durante años el lenguaje principal de Cocoa y del desarrollo para Mac e iPhone.

Para un proyecto nuevo no suele ser la primera elección frente a Swift, pero sigue apareciendo en enormes bases de código y frameworks.

Su sintaxis resulta inconfundible:

#import <Foundation/Foundation.h>

int main(void)
{
    @autoreleasepool {
        NSLog(@"Hello from Objective-C");
    }

    return 0;
}Lenguaje del código: PHP (php)

Puede compilarse desde Terminal enlazando Foundation:

clang hello.m \
  -framework Foundation \
  -o helloLenguaje del código: CSS (css)

Para programadores que trabajan con aplicaciones veteranas, agentes corporativos o código heredado de macOS, saber interpretar Objective-C sigue teniendo valor.

3. C++: rendimiento, herramientas y software multiplataforma

C++ tiene un encaje diferente.

Apple ofrece soporte oficial mediante Apple Clang y libc++, y mantiene documentación específica sobre el estado de las características del estándar C++.

Tiene especial sentido para:

  • motores de juegos;
  • renderizado;
  • simulación;
  • procesamiento multimedia;
  • bases de datos;
  • software científico;
  • herramientas CLI;
  • bibliotecas multiplataforma;
  • aplicaciones donde gran parte del código también debe compilar en Linux o Windows.

C++ también puede integrarse progresivamente con Swift.

Apple destaca la interoperabilidad Swift-C++ como una forma de mantener en C++ las partes críticas o multiplataforma y utilizar Swift para acceder al resto de los frameworks de la plataforma.

Este modelo resulta particularmente interesante para proyectos grandes.

Una interfaz de macOS puede estar escrita en Swift, mientras un motor compartido con Linux y Windows permanece en C++.

4. C: pequeño, estable y todavía difícil de evitar

C sigue presente en los niveles inferiores del sistema.

Aunque pocas aplicaciones gráficas nuevas de macOS se escribirían completamente en C, continúa siendo útil para:

  • utilidades UNIX;
  • bibliotecas;
  • APIs POSIX;
  • parsers;
  • software de red;
  • código embebido;
  • interoperabilidad entre lenguajes.

Un ejemplo:

#include <stdio.h>

int main(void)
{
    printf("Hello from C\n");
    return 0;
}Lenguaje del código: PHP (php)

Puede compilarse con:

clang hello.c -o helloLenguaje del código: CSS (css)

Para un administrador de sistemas que trabaje también con Linux, C conserva además una ventaja: una parte importante del conocimiento puede trasladarse entre ambos sistemas, siempre que el código no dependa de APIs exclusivas del kernel Linux.

5. Objective-C++: el puente que mucha gente olvida

Objective-C++ permite mezclar C++ y Objective-C dentro de un mismo archivo.

La extensión habitual es:

.mmLenguaje del código: CSS (css)

Esto permite que una aplicación mantenga su lógica en C++ y utilice desde una capa delgada APIs de Cocoa.

Puede ser especialmente útil en aplicaciones antiguas, motores y herramientas multiplataforma.

Conceptualmente:

Swift / Objective-C
        ↓
Objective-C++
        ↓
C++

No debería verse como un lenguaje para empezar una aplicación desde cero, sino como una herramienta de interoperabilidad.

6. Metal Shading Language: cuando el código debe ejecutarse en la GPU

Existe finalmente un lenguaje especializado que tiene mucho sentido mencionar dentro del entorno nativo de Apple: Metal Shading Language (MSL).

Metal es la API de Apple para gráficos y computación sobre GPU. Su lenguaje de shaders permite escribir kernels que serán ejecutados por la GPU de los dispositivos Apple.

Apple presenta Metal como una API estrechamente integrada con Apple Silicon para gráficos y cargas de computación, incluida parte del procesamiento relacionado con aprendizaje automático.

Un kernel de cómputo adopta una forma diferente a C++ convencional:

#include <metal_stdlib>
using namespace metal;

kernel void add_arrays(
    device const float* a [[buffer(0)]],
    device const float* b [[buffer(1)]],
    device float* result [[buffer(2)]],
    uint id [[thread_position_in_grid]])
{
    result[id] = a[id] + b[id];
}Lenguaje del código: PHP (php)

No es un sustituto de Swift ni C++. Es el lenguaje adecuado cuando la parte que se quiere programar se ejecutará sobre las GPU administradas mediante Metal.

C++ también puede interactuar directamente con Metal mediante metal-cpp, la interfaz C++ oficial que Apple proporciona para usar Metal desde proyectos escritos en ese lenguaje. Apple afirma que esta interfaz ofrece un mapeo directo de las clases y APIs de Metal sin overhead medible respecto a las cabeceras Objective-C correspondientes.

¿Y Rust?

Rust merece una aclaración porque cada vez aparece más en software de sistemas desarrollado desde un Mac.

Rust genera ejecutables nativos para macOS y Apple Silicon, pero no forma parte del conjunto de lenguajes que Apple presenta como núcleo de su toolchain de desarrollo de aplicaciones.

Puede ser una excelente elección para herramientas CLI, agentes, networking o componentes multiplataforma, especialmente cuando se valora la seguridad de memoria.

Pero si la pregunta concreta es cuáles son los lenguajes con integración más directa y oficial dentro del desarrollo Apple, Swift, Objective-C, C y C++ ocupan una posición diferente.

Algo parecido ocurre con Go, Zig o incluso Python: todos pueden utilizarse perfectamente en un Mac, pero «funcionar en macOS» y estar integrados de forma nativa en el modelo de desarrollo de Apple son conceptos distintos.

Lo que debería conocer un sysadmin que recibe un binario para macOS

Un administrador no necesita dominar C++ para diagnosticar muchos problemas relacionados con ejecutables.

Con cuatro o cinco comandos puede obtener bastante información.

Arquitectura:

file programa

Arquitecturas de un Universal Binary:

lipo -info programa

Bibliotecas dinámicas:

otool -L programa

Información de firma:

codesign -dv --verbose=4 programa

Verificación:

codesign --verify --verbose programa

Para obtener información sobre Gatekeeper:

spctl --assess --verbose programa

Y si el programa termina inesperadamente:

lldb ./programa

Esta parte diferencia bastante el trabajo cotidiano entre simplemente «saber compilar C++» y administrar software nativo en macOS.

Cuando bits/stdc++.h vuelve a aparecer

Código C++ copiado desde entornos GNU puede incluir:

#include <bits/stdc++.h>Lenguaje del código: HTML, XML (xml)

No debería esperarse que funcione con el entorno estándar de Apple.

bits/stdc++.h no pertenece al estándar ISO C++. Está asociada principalmente a GNU libstdc++.

La alternativa portable es declarar las cabeceras realmente utilizadas:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>Lenguaje del código: HTML, XML (xml)

El código queda así preparado para trabajar con Apple libc++ y resulta además más fácil de trasladar entre toolchains.

CMake para no convertir la línea de comandos en un proyecto

Con uno o dos archivos, utilizar directamente clang++ es cómodo.

Conforme aparecen dependencias y múltiples plataformas, tiene más sentido utilizar un sistema de build como CMake.

Un CMakeLists.txt mínimo:

cmake_minimum_required(VERSION 3.20)

project(MacTool LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(mactool src/main.cpp)Lenguaje del código: JavaScript (javascript)

Después:

cmake -S . -B build
cmake --build build

Esto facilita mantener el mismo proyecto para macOS y Linux sin escribir manualmente comandos clang++ distintos para cada archivo.

Para administradores que compilan herramientas internas o software open source, entender esta capa suele resultar más útil a medio plazo que memorizar decenas de parámetros del compilador.

La elección final del lenguaje depende de la capa que se quiera construir. Swift encaja mejor con las APIs modernas de Apple, C++ con motores y componentes de alto rendimiento, C con las capas inferiores y Objective-C con buena parte del legado Cocoa. Metal Shading Language aparece cuando el objetivo deja de ser la CPU y pasa a ser la GPU.

Por eso un Mac de desarrollo actual no debe verse como una máquina «para programar en Swift». También ofrece un toolchain UNIX completo capaz de construir software C y C++, depurarlo con LLDB, generar binarios ARM64 o universales y combinar distintos lenguajes dentro de una misma aplicación.

Preguntas frecuentes

¿Cuál es el mejor lenguaje para programar aplicaciones nativas de macOS?

Para una aplicación nueva centrada en las plataformas Apple, Swift es normalmente la elección principal y Apple lo presenta como su lenguaje preferente. C++ puede incorporarse cuando existen componentes multiplataforma o necesidades específicas de rendimiento.

¿Es mejor C++ o Swift para desarrollar en un Mac?

Depende del proyecto. Swift ofrece una integración más directa con los frameworks modernos de Apple, mientras C++ resulta especialmente útil en motores, bibliotecas, software científico, herramientas y código compartido con Linux o Windows.

¿Apple utiliza GCC o Clang para C++?

El entorno oficial de Apple utiliza Apple Clang junto con libc++. Clang está disponible tanto mediante Xcode como a través de las Command Line Tools.

¿Qué lenguajes pueden generar programas nativos para Apple Silicon?

Swift, Objective-C, C y C++ disponen de integración directa con el toolchain de Apple y pueden generar código ARM64. Otros lenguajes como Rust, Go o Zig también pueden producir ejecutables nativos para macOS, aunque utilizan sus propios toolchains y tienen un nivel diferente de integración con las APIs de Apple.

Fuentes:

  • Apple Developer, C++ Language Support.
  • Apple Developer, Swift y Swift Pathway.
  • Apple Developer, Development Process y Tools and Distribution.
  • Apple Developer, Metal y Metal-cpp.
  • Swift.org, documentación oficial del lenguaje Swift.

Suscríbete al boletín SysAdmin

Este es tu recurso para las últimas noticias y consejos sobre administración de sistemas, Linux, Windows, cloud computing, seguridad de la nube, etc. Lo enviamos 2 días a la semana. Recuerda revisar tu email para confirmar la suscripción.

¡Apúntate a nuestro newsletter!


– patrocinadores –

Noticias destacadas

– patrocinadores –

¡SUSCRÍBETE AL BOLETÍN
DE LOS SYSADMINS!

Scroll al inicio
×