C++ Usar Bibliotecas Externas
A biblioteca padrão do C++ é grande, mas não tem tudo: não comprime arquivos, não desenha janelas, não conversa com bancos de dados. Para isso existem milhares de bibliotecas externas, código pronto, testado e gratuito, escrito por outras pessoas. Usar uma em C++ dá um pouco mais de trabalho do que em outras linguagens, porque a biblioteca precisa chegar em duas etapas: os cabeçalhos na compilação e o código já compilado na ligação. Nesta aula você vai entender essas duas partes, instalar bibliotecas pelo apt, ligá-las com -l e deixar o pkg-config e o CMake encontrarem tudo por você.
- A diferença entre bibliotecas header-only e compiladas.
- Como achar e instalar uma biblioteca pelo
apt, e por que o pacote certo termina em-dev. - Como compilar e ligar com
-l(exemplo real: a zlib, com-lz), e por que a posição do-limporta. - Como o
pkg-configdescobre as opções certas. - Como o CMake faz isso com
find_package. - O que são os gerenciadores de pacotes vcpkg e Conan.
Os comandos desta aula foram executados na máquina fictícia da Ana (usuária ana, computador notebook), um Ubuntu 26.04 com o g++ 15.2 e o CMake 4.2. Nela, a biblioteca de compressão zlib já está instalada (com o pacote de desenvolvimento), e a biblioteca de formatação fmt não está; os exemplos com a fmt instalada estão marcados como ilustrativos.
A ideia: comprar pronto
A Ana quer pôr um bolo de casamento no cardápio da lojinha. Ela pode fazer tudo do zero, ou comprar a massa pronta de uma confeitaria e só montar. Uma biblioteca é isso: alguém já fez a parte difícil (e testou muito), e você só usa.
Em C++, uma biblioteca chega de um destes dois jeitos:
| Header-only ("só cabeçalho") | Compilada | |
|---|---|---|
| O que vem | só arquivos .h, com todo o código dentro (funções inline, templates) | arquivos .h (as declarações) e um arquivo binário (.so ou .a no Linux) com o código já compilado |
| Na compilação | #include | #include |
| Na ligação | nada | é preciso pedir a biblioteca: -l… |
| Vantagem | fácil: copie o arquivo para a pasta e pronto | compila rápido (o código pesado já está pronto) |
| Exemplos | doctest, nlohmann/json, a maior parte do Boost | zlib, SQLite, OpenSSL, Qt, fmt (que também tem um modo header-only) |
Você já usou uma header-only na aula Testes Automatizados: a doctest é um único arquivo doctest.h, baixado para a pasta do projeto. E se lembra das duas partes de um projeto com vários arquivos (Arquivos .h e .cpp)? Uma biblioteca compilada é a mesma coisa, só que o .cpp foi compilado por outra pessoa e você recebe o resultado pronto.
1. Header-only: só incluir
Uma biblioteca header-only é simplesmente um .h com tudo dentro. Para ver como é por dentro, a Ana escreveu uma bem pequena, para mostrar preços em reais:
#pragma once
// moeda.h: uma "biblioteca" header-only. Tudo está aqui; não há nada para ligar.
#include <format>
#include <string>
namespace moeda {
inline std::string reais(double valor) {
std::string texto = std::format("{:.2f}", valor); // por exemplo, "27.50"
for (char& c : texto) {
if (c == '.') {
c = ',';
}
}
return "R$ " + texto;
}
}
Repare nos ingredientes: #pragma once, um namespace próprio (para não colidir com nomes de quem usa) e funções inline (para poderem ser incluídas por vários .cpp sem o erro "múltiplas definições"). É exatamente o que as bibliotecas header-only de verdade fazem, só que com milhares de linhas.
Experimente: a aba moeda.h é a biblioteca, e o main.cpp só a inclui. Rode. Depois acrescente à biblioteca uma função inline std::string centavos(int c) que devolve reais(c / 100.0) e use-a no main com moeda::centavos(1990).
#include <iostream>
#include "moeda.h"
int main() {
std::cout << "Pão: " << moeda::reais(0.75) << '\n';
std::cout << "Total: " << moeda::reais(27.5) << '\n';
}
#pragma once
// moeda.h: uma "biblioteca" header-only. Tudo está aqui; não há nada para ligar.
#include <format>
#include <string>
namespace moeda {
inline std::string reais(double valor) {
std::string texto = std::format("{:.2f}", valor); // por exemplo, "27.50"
for (char& c : texto) {
if (c == '.') {
c = ',';
}
}
return "R$ " + texto;
}
}
2. Instalar uma biblioteca compilada pelo apt
No Ubuntu, as bibliotecas mais conhecidas estão no repositório de pacotes, prontas para instalar com o apt (veja o Instalar no Linux). Cada biblioteca costuma vir em dois pacotes:
| Pacote | Exemplo | O que traz | Quem precisa |
|---|---|---|---|
| a biblioteca (runtime) | libfmt10 | só o binário .so | quem roda programas que usam a biblioteca |
o de desenvolvimento, terminado em -dev | libfmt-dev | os cabeçalhos .h, o .so para ligar, o arquivo do pkg-config e o do CMake | quem compila programas com a biblioteca: você |
Procurar e conferir
Para achar o nome do pacote, use apt-cache search. A opção --names-only procura só nos nomes, e o ^ quer dizer "começando com":
$ apt-cache search --names-only '^libfmt' libfmt-dev - fast type-safe C++ formatting library -- development files libfmt-doc - fast type-safe C++ formatting library -- documentation libfmt-ocaml - OCaml Format pretty-printer combinators (runtime) libfmt-ocaml-dev - OCaml Format pretty-printer combinators (development) libfmt10 - fast type-safe C++ formatting library -- library $ apt-cache show libfmt-dev | grep -E '^(Description|Homepage)' | head -3 Homepage: https://fmt.dev/ Description-en: fast type-safe C++ formatting library -- development files Description-md5: 7963a4ca548a609af43ee9bd78ee3acd
Os dois libfmt-ocaml são de outra linguagem, com nome parecido: leia a descrição. O que interessa é o libfmt-dev ("arquivos de desenvolvimento"). Para saber se ele está instalado e qual versão viria, use apt-cache policy:
$ apt-cache policy libfmt-dev
libfmt-dev:
Instalado: (nenhum)
Candidato: 10.1.1+ds1-4build1
Tabela de versão:
10.1.1+ds1-4build1 500
500 http://archive.ubuntu.com/ubuntu resolute/universe amd64 Packages
- Instalado: (nenhum)
- Não está instalado.
- Candidato: 10.1.1+ds1-4build1
- A versão que o
apt installinstalaria: a fmt 10.1.1 (o resto é a numeração do pacote no Ubuntu). - resolute/universe
- Vem do repositório universe do Ubuntu 26.04 (codinome resolute), mantido pela comunidade.
Compare com a zlib, que a Ana já tem:
$ apt-cache policy zlib1g-dev
zlib1g-dev:
Instalado: 1:1.3.dfsg+really1.3.1-1ubuntu3.1
Candidato: 1:1.3.dfsg+really1.3.1-1ubuntu3.1
Tabela de versão:
*** 1:1.3.dfsg+really1.3.1-1ubuntu3.1 500
500 http://archive.ubuntu.com/ubuntu resolute-updates/main amd64 Packages
500 http://security.ubuntu.com/ubuntu resolute-security/main amd64 Packages
100 /var/lib/dpkg/status
1:1.3.dfsg+really1.3.1-1ubuntu3 500
500 http://archive.ubuntu.com/ubuntu resolute/main amd64 Packages
Os *** marcam a versão instalada (a zlib 1.3.1, com uma atualização de segurança).
Ver o que seria instalado, sem instalar
apt-get -s install faz uma simulação (-s): mostra o que aconteceria, sem mudar nada e sem pedir senha. É um bom hábito antes de instalar:
$ apt-get -s install libfmt-dev
NOTA: Esta é apenas uma simulação!
apt-get precisa de privilégios de root para execução real.
Lembre-se também de que a trava está desativada e,
portanto, não dependa da relevância para a situação real atual!
Lendo listas de pacotes...
Construindo árvore de dependências...
Lendo informação de estado...
Resolvendo dependências...
Os pacotes adicionais a seguir serão instalados:
libfmt10
Pacotes sugeridos:
libfmt-doc
Os NOVOS pacotes a seguir serão instalados:
libfmt-dev libfmt10
0 pacotes atualizados, 2 pacotes novos instalados, 0 a serem removidos e 12 não atualizados.
Inst libfmt10 (10.1.1+ds1-4build1 Ubuntu:26.04/resolute [amd64])
Inst libfmt-dev (10.1.1+ds1-4build1 Ubuntu:26.04/resolute [amd64])
Conf libfmt10 (10.1.1+ds1-4build1 Ubuntu:26.04/resolute [amd64])
Conf libfmt-dev (10.1.1+ds1-4build1 Ubuntu:26.04/resolute [amd64])
Pedindo o libfmt-dev, o apt traz junto o libfmt10 (a biblioteca em si), como explicado na tabela acima. As linhas Inst (instalar) e Conf (configurar) são o plano. Para instalar de verdade: sudo apt install libfmt-dev.
O que tem dentro de um pacote -dev
O dpkg -L lista os arquivos de um pacote instalado. Filtrando os que interessam, da zlib:
$ dpkg -L zlib1g-dev | grep -E '\.(h|so|a|pc)$' /usr/include/zconf.h /usr/include/zlib.h « o cabeçalho que você inclui /usr/lib/x86_64-linux-gnu/libz.a « a biblioteca estática /usr/lib/x86_64-linux-gnu/pkgconfig/zlib.pc « o arquivo do pkg-config /usr/share/doc/zlib1g-dev/examples/gzlog.h /usr/share/doc/zlib1g-dev/examples/zran.h /usr/lib/x86_64-linux-gnu/libz.so « a biblioteca compartilhada
O zlib.h está em /usr/include, uma das pastas onde o #include <…> procura (lembra da lista em O Pré-processador?). Por isso basta #include <zlib.h>, sem -I.
3. Compilar e ligar com -l
A zlib é a biblioteca de compressão por trás do formato .gz, do PNG e de boa parte da internet. A Ana a usou para ver quanto uma lista de compras repetida encolhe:
#include <iostream>
#include <string>
#include <vector>
#include <zlib.h>
int main() {
std::string texto;
for (int i = 0; i < 50; ++i) {
texto += "pão, leite, café; ";
}
uLongf tamanho = compressBound(texto.size());
std::vector<Bytef> comprimido(tamanho);
int resultado = compress(comprimido.data(), &tamanho,
reinterpret_cast<const Bytef*>(texto.data()), texto.size());
if (resultado != Z_OK) {
std::cout << "Erro ao comprimir: " << resultado << '\n';
return 1;
}
std::cout << "zlib versão " << zlibVersion() << '\n';
std::cout << "Original: " << texto.size() << " bytes\n";
std::cout << "Comprimido: " << tamanho << " bytes\n";
}
- #include <zlib.h>
- As declarações da zlib: as funções
compress,compressBound,zlibVersione os tiposuLongfeBytef(números e bytes, nos nomes que a zlib usa). - compressBound(texto.size())
- "No pior caso, quantos bytes a versão comprimida pode ter?" Usado para reservar espaço no
vector. - compress(destino, &tamanho, origem, tamanhoOrigem)
- Comprime. A zlib é uma biblioteca em C, que trabalha com bytes e ponteiros: o
reinterpret_cast<const Bytef*>diz "trate as letras do texto como bytes", e o&tamanhodeixa a função escrever notamanhoquantos bytes realmente usou. - Z_OK
- A constante que a zlib devolve quando deu certo. Bibliotecas em C avisam erros assim, por um número.
Compilando do jeito de sempre:
$ g++ -std=c++23 -Wall -Wextra zip.cpp -o zip /usr/bin/x86_64-linux-gnu-ld.bfd: /tmp/ccdRfd5K.o: na função "main": zip.cpp:(.text+0x6e): referência não definida para "compressBound" /usr/bin/x86_64-linux-gnu-ld.bfd: zip.cpp:(.text+0xd9): referência não definida para "compress" /usr/bin/x86_64-linux-gnu-ld.bfd: zip.cpp:(.text+0x152): referência não definida para "zlibVersion" collect2: error: ld returned 1 exit status
A compilação passou: o zlib.h foi achado e declarou as funções. Quem falhou foi a ligação: é o undefined reference ("referência não definida") de Compilar Vários Arquivos. O corpo das funções está no libz.so, e ninguém pediu ao ligador para olhar lá. É para isso que serve o -l:
$ g++ -std=c++23 -Wall -Wextra zip.cpp -o zip -lz $ ./zip zlib versão 1.3.1 Original: 1000 bytes Comprimido: 38 bytes
Mil bytes (50 repetições de 20 bytes; o ã e o é ocupam 2 bytes cada em UTF-8) viraram 38: texto repetido comprime muito bem.
- -lz
-l(de library, "biblioteca") seguido do nome curto da biblioteca, colado. O ligador transforma-lzemlibz.so(oulibz.a): põelibna frente e a extensão no fim. Do mesmo jeito,-lfmtprocuralibfmt.soe-lsqlite3procuralibsqlite3.so.- a posição: no fim
- Depois dos arquivos que usam a biblioteca. Veja o porquê logo abaixo.
Os arquivos que o -lz pode encontrar:
$ ls /usr/lib/x86_64-linux-gnu/libz.* /usr/lib/x86_64-linux-gnu/libz.a /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libz.so.1 /usr/lib/x86_64-linux-gnu/libz.so.1.3.1
| Arquivo | Tipo | Como funciona |
|---|---|---|
libz.so (shared object) | biblioteca compartilhada (dinâmica) | o programa guarda só uma referência; a biblioteca é carregada quando ele roda. Um programa menor, e todos os programas do sistema usam a mesma cópia (e recebem as atualizações de segurança dela). É a escolhida por padrão. |
libz.a (archive) | biblioteca estática | um pacote de arquivos .o, copiados para dentro do programa na ligação. O programa fica maior, mas não depende de nada na hora de rodar. |
libz.so.1, libz.so.1.3.1 | a mesma .so, com a versão no nome | atalhos (links) para a versão instalada |
O comando ldd lista as bibliotecas compartilhadas de que um programa precisa para rodar:
$ ldd ./zip | grep libz libz.so.1 => /usr/lib/x86_64-linux-gnu/libz.so.1 (0x0000788107b45000)
(O número entre parênteses é o endereço onde ela foi carregada na memória, que muda a cada execução.) É por isso que existe o pacote zlib1g, sem -dev: quem só roda o zip precisa do libz.so.1, não dos cabeçalhos.
-l no lugar errado
$ g++ -std=c++23 -Wall -Wextra -lz zip.cpp -o zip2 /usr/bin/x86_64-linux-gnu-ld.bfd: /tmp/ccMg9oec.o: na função "main": zip.cpp:(.text+0x6e): referência não definida para "compressBound" /usr/bin/x86_64-linux-gnu-ld.bfd: zip.cpp:(.text+0xd9): referência não definida para "compress" /usr/bin/x86_64-linux-gnu-ld.bfd: zip.cpp:(.text+0x152): referência não definida para "zlibVersion" collect2: error: ld returned 1 exit status
O que aconteceu: o -lz está lá, mas antes do zip.cpp. O ligador lê o comando da esquerda para a direita. Quando chegou na zlib, ainda ninguém tinha pedido nada dela (o zip.cpp vem depois), e ele a deixou de lado. Quando o zip.cpp pediu as funções, a zlib já tinha ficado para trás.
Como resolver: as bibliotecas vão depois dos arquivos que as usam; na dúvida, no fim do comando. É a exceção à regra "a ordem não importa", que vale para .cpp e .o.
$ g++ -std=c++23 -Wall -Wextra zip.cpp -o zip -lzlib /usr/bin/x86_64-linux-gnu-ld.bfd: não foi possível localizar -lzlib: Arquivo ou diretório inexistente collect2: error: ld returned 1 exit status
O que significa: o ligador procurou libzlib.so e libzlib.a, que não existem (em inglês: cannot find -lzlib). O nome do -l é o do arquivo (libz), não o do projeto (zlib).
Como resolver: descubra o nome do arquivo com dpkg -L pacote-dev ou, melhor, pergunte ao pkg-config (próxima seção). Se o erro for com o nome certo, falta instalar o pacote -dev.
A Ana tentou um exemplo da fmt sem ter o libfmt-dev:
$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola -lfmt
ola.cpp:1:10: fatal error: fmt/core.h: Arquivo ou diretório inexistente
1 | #include <fmt/core.h>
| ^~~~~~~~~~~~
compilation terminated.
O que significa: o erro acontece antes de chegar no -lfmt: o cabeçalho não existe em nenhuma pasta do sistema. (O fmt/ no nome quer dizer que os cabeçalhos da fmt ficam numa subpasta, /usr/include/fmt/.)
Como resolver: sudo apt install libfmt-dev. Se a biblioteca foi instalada em outro lugar (compilada à mão, por exemplo), indique as pastas: -I para os cabeçalhos e -L para os binários, como em g++ … -I/opt/fmt/include … -L/opt/fmt/lib -lfmt.
4. pkg-config: "quais opções essa biblioteca precisa?"
Cada biblioteca tem as suas manias: uma precisa de -I com uma pasta especial, outra de duas -l, outra de uma opção de compilação. Decorar isso não dá. O pkg-config é um programa que guarda essas receitas: cada pacote -dev instala um arquivo .pc (como o zlib.pc que apareceu no dpkg -L), e você pergunta:
$ pkg-config --modversion zlib 1.3.1 $ pkg-config --cflags --libs zlib -lz
- --modversion
- A versão instalada.
- --cflags
- As opções de compilação (por exemplo,
-I…). A zlib não precisa de nenhuma, porque o cabeçalho está em/usr/include; por isso só sai um espaço. - --libs
- As opções de ligação:
-lz.
O truque é pôr a pergunta dentro do comando do g++ com $( … ) (a "substituição de comando" do terminal, que troca o trecho pela saída dele):
$ g++ -std=c++23 -Wall -Wextra zip.cpp -o zip3 $(pkg-config --cflags --libs zlib) $ ./zip3 | head -1 zlib versão 1.3.1
Assim, o comando funciona para qualquer biblioteca com .pc, sem você saber os detalhes. Para ver quantas o sistema conhece: pkg-config --list-all (na máquina da Ana, 84). Se o pacote não estiver instalado:
$ pkg-config --libs fmt Package fmt was not found in the pkg-config search path. Perhaps you should add the directory containing `fmt.pc' to the PKG_CONFIG_PATH environment variable Package 'fmt' not found $ echo $? 1
"O pacote fmt não foi encontrado." O conselho sobre o PKG_CONFIG_PATH só serve para bibliotecas instaladas fora do lugar padrão; aqui, falta mesmo o sudo apt install libfmt-dev, que instala o fmt.pc. Depois disso, o mesmo comando daria as opções da fmt.
Com a fmt instalada, o exemplo dela compilaria assim (ilustrativo: a fmt não está instalada na máquina da Ana):
#include <fmt/core.h>
int main() {
fmt::print("Olá, {}! Você tem {} itens.\n", "Ana", 3);
}
$ g++ -std=c++23 -Wall -Wextra ola.cpp -o ola $(pkg-config --cflags --libs fmt) « ilustrativo $ ./ola Olá, Ana! Você tem 3 itens.
A fmt foi a inspiração do std::format e do std::print, que entraram na biblioteca padrão no C++20 e no C++23. Com o g++ 15 e -std=c++23, você já tem quase tudo isso sem biblioteca externa. Muitos projetos ainda usam a fmt por terem nascido antes, ou para compiladores mais antigos.
5. No CMake: find_package
No CMake, a pergunta "onde está a biblioteca e como ligo?" é feita com find_package ("encontre o pacote"), e a ligação, com o target_link_libraries que você já conhece:
cmake_minimum_required(VERSION 3.25) project(Zip LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) find_package(ZLIB REQUIRED) add_executable(zip zip.cpp) target_link_libraries(zip PRIVATE ZLIB::ZLIB)
- find_package(ZLIB REQUIRED)
- Procura a zlib no sistema.
REQUIRED("obrigatório"): se não achar, pare com erro. O nome (ZLIB, em maiúsculas) é o que a documentação da biblioteca indica. - ZLIB::ZLIB
- Um alvo importado, criado pelo
find_package, que carrega tudo o que a biblioteca precisa: pastas de cabeçalhos, o arquivo para ligar e opções. O::no nome é a marca desses alvos.
$ cmake -B build -G Ninja -- The CXX compiler identification is GNU 15.2.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Found ZLIB: /usr/lib/x86_64-linux-gnu/libz.so (found version "1.3.1") -- Configuring done (0.4s) -- Generating done (0.0s) -- Build files have been written to: /home/ana/cpp/libs/build $ cmake --build build [1/2] Building CXX object CMakeFiles/zip.dir/zip.cpp.o [2/2] Linking CXX executable zip $ ./build/zip zlib versão 1.3.1 Original: 1000 bytes Comprimido: 38 bytes
Repare na linha -- Found ZLIB: ("encontrei a zlib"), com o caminho e a versão. Para a fmt, o CMakeLists.txt seria igual, com find_package(fmt REQUIRED) e target_link_libraries(ola PRIVATE fmt::fmt). Sem o libfmt-dev, a configuração para assim:
$ cmake -B build -G Ninja
«… detecções do compilador …»
CMake Error at CMakeLists.txt:6 (find_package):
By not providing "Findfmt.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "fmt", but
CMake did not find one.
Could not find a package configuration file provided by "fmt" with any of
the following names:
fmtConfig.cmake
fmt-config.cmake
Add the installation prefix of "fmt" to CMAKE_PREFIX_PATH or set "fmt_DIR"
to a directory containing one of the above files. If "fmt" provides a
separate development package or SDK, be sure it has been installed.
-- Configuring incomplete, errors occurred!
A mensagem é longa, mas a última frase é a que importa: "se o fmt tem um pacote de desenvolvimento separado, confira se ele está instalado". O fmtConfig.cmake que o CMake procurou vem dentro do libfmt-dev. Depois do sudo apt install libfmt-dev, a configuração passaria, mostrando a fmt encontrada (ilustrativo).
6. Gerenciadores de pacotes: vcpkg e Conan
O apt resolve bem no Linux, mas tem limites: a versão é a que o Ubuntu escolheu (a fmt 10.1, enquanto o projeto pode querer outra), e não existe no Windows nem no macOS. Para isso surgiram os gerenciadores de pacotes próprios do C++:
| Ferramenta | De quem | Como funciona |
|---|---|---|
| vcpkg | Microsoft (livre, multiplataforma) | você lista as bibliotecas num arquivo vcpkg.json; o vcpkg baixa e compila cada uma, e o CMake as encontra pelo mesmo find_package |
| Conan | JFrog (livre, multiplataforma) | você lista as bibliotecas num conanfile.txt ou conanfile.py; ele baixa versões já compiladas quando existem e gera os arquivos para o CMake |
FetchContent do CMake | o próprio CMake | o CMakeLists.txt baixa o código da biblioteca (do GitHub, por exemplo) e a compila junto com o projeto |
Para projetos de estudo no Linux, o apt basta. Quando o projeto precisar rodar em vários sistemas ou de versões exatas, vale aprender um desses.
- A biblioteca padrão já faz?
std::format,std::filesystem,std::chrono,<random>… cobrem muita coisa. - Está viva? Atualizações recentes, documentação, gente usando.
- A licença permite o seu uso? MIT, BSD, zlib e Boost são bem permissivas.
- Está no
apt, no vcpkg ou no Conan? Isso facilita muito a vida.
Referência
| Comando | O que faz |
|---|---|
apt-cache search --names-only '^lib…' | procura pacotes pelo nome |
apt-cache policy pacote | instalado? qual versão viria? |
apt-get -s install pacote | simula a instalação |
sudo apt install pacote-dev | instala a biblioteca para desenvolvimento |
dpkg -L pacote | lista os arquivos de um pacote instalado |
g++ … arquivo.cpp -o prog -lnome | liga com libnome.so (o -l depois dos arquivos) |
-I pasta / -L pasta | pasta extra de cabeçalhos / de bibliotecas |
pkg-config --cflags --libs nome | as opções certas para a biblioteca |
ldd ./prog | as bibliotecas compartilhadas de que o programa precisa |
find_package(Nome REQUIRED) | (CMake) procura a biblioteca |
target_link_libraries(prog PRIVATE Nome::Nome) | (CMake) liga com ela |
| Mensagem | Fase | Causa |
|---|---|---|
fatal error: x.h: Arquivo ou diretório inexistente | compilação | pacote -dev não instalado, ou falta -I |
referência não definida para / undefined reference to | ligação | faltou o -l, ou ele está antes dos arquivos |
não foi possível localizar -lx / cannot find -lx | ligação | nome errado no -l, pacote não instalado, ou falta -L |
Package x was not found in the pkg-config search path | pkg-config | pacote -dev não instalado |
Could not find a package configuration file provided by "x" | CMake | pacote -dev não instalado |
Exercícios
1. O nome do -l
O arquivo da biblioteca SQLite se chama libsqlite3.so. Complete a opção de ligação:
2. Perguntar ao pkg-config
Complete para obter as opções de compilação e de ligação da zlib:
3. Qual pacote instalar para compilar programas com a fmt?
4. g++ -lz zip.cpp -o zip dá referência não definida para "compress", mas g++ zip.cpp -o zip -lz funciona. Por quê?
5. O que caracteriza uma biblioteca header-only?
Missão final: da fmt para a biblioteca padrão
Missão: o editor do guia não tem a fmt instalada, como a máquina da Ana. Rode e veja o fatal error do cabeçalho que não existe (o Clang diz 'fmt/core.h' file not found, "arquivo não encontrado"). Depois troque a fmt pela biblioteca padrão do C++23, que tem a mesma ideia: o cabeçalho <print> e a função std::print (com {} no lugar de cada valor). A saída deve ser Olá, Ana! Você tem 3 itens.
#include <fmt/core.h>
int main() {
fmt::print("Olá, {}! Você tem {} itens.\n", "Ana", 3);
}
#include <print>
int main() {
std::print("Olá, {}! Você tem {} itens.\n", "Ana", 3);
}
Resumo
- Uma biblioteca header-only é só
.h: copie e inclua. Uma compilada tem.h(para compilar) e.so/.a(para ligar). - No Ubuntu, instale o pacote
-dev(sudo apt install libfmt-dev). Antes, confira comapt-cache policye simule comapt-get -s install. - Ligue com
-lnome(procuralibnome.so), depois dos arquivos:g++ zip.cpp -o zip -lz. Sem ele:undefined reference("referência não definida"). pkg-config --cflags --libs nomedá as opções certas; use dentro do comando com$( … ).- No CMake:
find_package(Nome REQUIRED)+target_link_libraries(prog PRIVATE Nome::Nome). - vcpkg, Conan e o
FetchContentdo CMake trazem bibliotecas em qualquer sistema e com a versão que você escolher.