4.8 KiB
4.8 KiB
Frameworks y librerías del ecosistema C/C++
Qt (C++, no C)
- Framework multiplataforma en C++: interfaces gráficas, red, hilos, bases de datos, impresión, multimedia. Funciona en Windows, Linux, macOS, Android, iOS y sistemas embebidos.
- Lo usan KDE, OBS Studio, Telegram Desktop, VirtualBox y muchos sistemas de coche y de punto de venta.
- Señales y slots: un objeto emite un evento y otros lo reciben. Para
implementarlo, Qt tiene un precompilador propio (
moc) que genera código C++ adicional antes de compilar. - QML: lenguaje declarativo, parecido a JS, para las interfaces modernas.
- Licencia: LGPLv3 o comercial (algunos módulos solo GPL o comercial). Con LGPL hay que permitir que el usuario sustituya las librerías de Qt, y por eso las aplicaciones Qt se distribuyen con sus DLL al lado.
- Relación con este proyecto: una aplicación Qt imprime con
QPrinter, que pasa por el driver del fabricante y el spooler. Para ESC/POS en crudo (cajón, corte, estado) necesitaría una librería como esta.
Por qué en C casi no hay "frameworks"
- La librería estándar de C es mínima: no trae red, ni contenedores, ni JSON,
ni HTTP. Los hilos (
threads.h) llegaron en C11 y son opcionales. - No hay gestor de paquetes oficial ni genéricos.
- Cultura resultante: librerías pequeñas que se combinan. Muchas son de
"una sola cabecera" o de un solo
.c, para poder copiarlas en el proyecto sin más.
En Go: la librería estándar ya trae net/http, encoding/json, sync,
etc., más go get.
Librerías C relevantes
| Librería | Qué es | Relación |
|---|---|---|
| GTK + GLib/GObject | Interfaz gráfica de GNOME y GIMP. GObject implementa orientación a objetos a mano sobre C | Ejemplo de cómo simular clases e interfaces en C |
| SDL | Ventanas, gráficos, audio y entrada; multiplataforma, incluido Android | Juegos y multimedia |
| LVGL | Interfaces gráficas para microcontroladores con pantalla | Terminales de punto de venta embebidos |
| libuv | Bucle de eventos multiplataforma | Es el motor de Node.js: lo que hay debajo de JS |
| libcurl | Cliente HTTP y muchos protocolos más | |
| SQLite | Base de datos completa en un solo .c |
Código C de referencia, muy bien probado |
| libusb | Acceso a USB desde modo usuario | Candidata para las fases 5 y 9 |
| zlib, OpenSSL | Compresión y criptografía | |
| Unity, cmocka | Frameworks de tests para C | Para probar la librería |
| stb | Colección de librerías de una sola cabecera (por ejemplo, stb_image para cargar PNG o JPEG) |
Posible ayuda en la fase 7 (imágenes) |
Interfaces en C
Una interface de Go se implementa en C como un struct de punteros a
función. Así funcionan GObject, los drivers de Linux y, en la fase 4, la
abstracción de transportes de la librería (LAN, USB) detrás de una misma API.
Piezas
- Puntero a función: las funciones también están en memoria (en la
sección
.text; la dirección que muestranmjunto a cadaT). Un puntero a función guarda esa dirección y permite llamarla. Sintaxis:double (*area)(const void *self);. void *: un puntero "a cualquier cosa", sin tipo. Hace el papel del receptor del método. C no tiene métodos: el objeto se pasa explícitamente. Go hace lo mismo por dentro:c.Area()se compila comoArea(c).- vtable: un
structcon los punteros a función de un tipo concreto. Se define una vez por tipo y la comparten todas sus instancias. - Valor de la "interfaz": dos punteros,
{vtable, datos}.
Cómo es una interfaz de Go por dentro
En el runtime de Go, una interfaz con métodos es exactamente eso:
type iface struct {
tab *itab // tipo concreto + punteros a sus métodos
data unsafe.Pointer // el valor concreto
}
Por eso una interfaz ocupa 16 bytes en 64 bits. Y por eso existe la trampa
del nil:
var p *MyError = nil
var err error = p
err != nil // true: tab apunta al tipo *MyError, aunque data sea nil
Una interfaz solo es nil si los dos campos son nil.
Qué hace Go por ti y qué haces tú en C
| Go | C |
|---|---|
| Comprueba en compilación que el tipo implementa la interfaz | Nadie lo comprueba: rellenas la vtable a mano |
Construye el itab automáticamente |
Declaras la vtable como static const |
| El receptor tiene su tipo | void *: si pasas el objeto equivocado, comportamiento indefinido |
| Un método que falta no compila | Un puntero a NULL en la vtable provoca un fallo al llamarlo |
Ejemplo real: los drivers de Linux
Cada driver rellena un struct file_operations con punteros a sus funciones
open, read, write... Cuando un programa hace write() sobre
/dev/usb/lp0, el núcleo llama a usblp_write a través de ese puntero. Es
el io.Writer del núcleo de Linux.