1.5 ABCg
Para facilitar o desenvolvimento das atividades práticas utilizaremos a biblioteca ABCg desenvolvida especialmente para esta disciplina.
A ABCg permite a prototipagem rápida de aplicações gráficas interativas 3D em C++ capazes de rodar tanto no desktop (binário nativo) quanto no navegador (binário WebAssembly).
Internamente a ABCg utiliza a biblioteca SDL para gerenciar o acesso a dispositivos de entrada (mouse/teclado/gamepad) e saída (vídeo e áudio) de forma independente de plataforma, e a biblioteca GLEW para acesso às funções da API gráfica OpenGL. Além disso, a API do Emscripten é utilizada sempre que a aplicação é compilada para gerar binário WebAssembly.
A ABCg é mais propriamente um framework do que uma biblioteca de funções, pois assume o controle da aplicação. Por outro lado, a camada de abstração para as APIs utilizadas é mínima e é possível acessar as funções da SDL e OpenGL diretamente (e faremos isso sempre que possível). Outras bibliotecas também utilizadas e que podem ser acessadas diretamente são:
- CPPIterTools: para o suporte a laços range-based em C++ usando funções do tipo
range
,enumerate
ezip
similares às do Python; - Dear ImGui: para gerenciamento de widgets de interface gráfica do usuário, tais como janelas, botões e caixas de edição;
- {fmt}: como alternativa mais eficiente ao stdio da linguagem C (
printf
,scanf
, etc) e iostreams do C++ (std::cout
,std::cin
, etc), e para formatação de strings com uma sintaxe similar ao str-format do Python; - Guidelines Support Library (GSL): para uso de funções e tipos de dados recomendados pelo C++ Core Guidelines;
- OpenGL Mathematics (GLM): para suporte a operações de transformação geométrica com vetores e matrizes;
- tinyobjloader: para a leitura de modelos 3D no formato Wavefront OBJ.
A seguir veremos como instalar e compilar a ABCg junto com um exemplo de uso.
Instalação
Em um terminal, clone o repositório do GitHub:
git clone https://github.com/hbatagelo/abcg.git
A versão mais recente da ABCg (atualmente v2.0.0) também pode ser baixada como um arquivo compactado de https://github.com/hbatagelo/abcg/releases/latest.
No Windows, certifique-se de clonar o repositório em um diretório cujo nome não contenha espaços ou caracteres especiais. Por exemplo, clone em C:\cg
em vez de C:\computação gráfica
.
O repositório tem a estrutura mostrada a seguir. Para simplificar, os arquivos e subdiretórios .git*
foram omitidos:
abcg
│ .clang-format
│ .clang-tidy
│ build.bat
│ build.sh
│ build-wasm.bat
│ build-wasm.sh
│ CMakeLists.txt
│ LICENSE
│ README.md
│ runweb.bat
│ runweb.sh
│ VERSION.md
│
└───abcg
│ │ ...
│
└───cmake
│ │ ...
│
└───examples
│ │ ...
│
└───public
│ ...
Os arquivos .clang-format
e .clang-tidy
são arquivos de configuração utilizados pelas ferramentas ClangFormat (formatação) e Clang-Tidy (linter) caso estejam instaladas.
Os arquivos .sh
são shell scripts de compilação e execução em linha de comando. Note que há scripts correspondentes com extensão .bat
para usar no Prompt de Comando do Windows (o PowerShell não é suportado):
build.sh
: para compilar a biblioteca e os exemplos em binários nativos;build-wasm.sh
: similar aobuild.sh
, mas para gerar binário em WebAssembly dentro do subdiretóriopublic
;runweb.sh
: para rodar um servidor web local que serve o conteúdo depublic
.
O arquivo CMakeLists.txt
é o script de configuração utilizado internamente pelo CMake.
Os subdiretórios são os seguintes:
abcg
contém o código-fonte da biblioteca e suas dependências;cmake
contém scripts auxiliares de configuração do CMake;examples
contém um exemplo de uso da ABCg: um “Hello, World!” que usa OpenGL e interface da ImGui;public
contém páginas web para exibir o exemplo “Hello, World!” no navegador.
Compilando em linha de comando
Execute o script build.sh
(Linux/macOS) ou build.bat
(Windows) para iniciar o processo de configuração e construção. A saída será similar a esta (o exemplo a seguir é do Windows):
-- The C compiler identification is GNU 10.3.0
-- The CXX compiler identification is GNU 10.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/msys64/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/msys64/mingw64/bin/g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
Using ccache
-- Found OpenGL: opengl32
-- Found GLEW: C:/msys64/mingw64/lib/cmake/glew/glew-config.cmake
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE
-- Found SDL2: mingw32;-mwindows;C:/msys64/mingw64/lib/libSDL2main.a;C:/msys64/mingw64/lib/libSDL2.dll.a
-- Found SDL2_image: C:/msys64/mingw64/lib/libSDL2_image.dll.a
-- Configuring done
-- Generating done
-- Build files have been written to: C:/abcg/build
...
[22/22] Linking CXX executable bin\helloworld.exe
Ao final, os binários estarão disponíveis no subdiretório build
. A biblioteca estática estará em build/abcg/libabcg.a
e o executável do exemplo “Hello, World!” estará em build/bin/helloworld
.
Para testar, execute o helloworld
. No Linux/macOS:
./build/bin/helloworld/helloworld
No Windows:
.\build\bin\helloworld\helloworld.exe | cat
No Windows, a saída deve sempre ser redirecionada para cat
ou tee
. Se isso não for feito, nenhuma saída de texto será exibida no terminal. Isso se deve a um bug do MSYS2.
Observe o conteúdo de build.sh
(build.bat
contém instruções equivalentes):
#!/bin/bash
set -euo pipefail
BUILD_TYPE=Debug
# Reset build directory
rm -rf build
mkdir -p build && cd build
# Configure
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
# Build
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
NUM_PROCESSORS=$(sysctl -n hw.ncpu)
else
NUM_PROCESSORS=$(nproc)
fi
cmake --build . --config $BUILD_TYPE -- -j $NUM_PROCESSORS
A variável
BUILD_TYPE
está comoDebug
, mas pode ser modificada paraRelease
,MinSizeRel
ouRelWithDebInfo
. Use a opçãoDebug
(padrão) ouRelWithDebInfo
enquanto estiver depurando o código. UseRelease
para gerar um binário otimizado e sem arquivos de símbolos de depuração (otimiza para gerar código mais rápido) ouMinSizeRel
(otimiza para gerar binário de menor tamanho).Observe que o script apaga o subdiretório
build
antes de criá-lo novamente. Portanto, não salve arquivos dentro debuild
pois eles serão apagados na próxima compilação!A geração dos binários usando o CMake é composta de duas etapas: configuração (
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
) e construção (cmake --build . --config $BUILD_TYPE
). A configuração gera os scripts do sistema de compilação nativo (por exemplo, arquivos Makefile ou Ninja). A construção dispara a compilação e ligação usando tais scripts. Todos os arquivos gerados na configuração e construção ficam armazenados no subdiretóriobuild
.
Compilando no Visual Studio Code
Primeiramente, clone o repositório abcg do GitHub como mostrado na seção anterior. Apague o subdiretório build
caso você já tenha compilado via linha de comando.
No Visual Studio Code, selecione o menu “File > Open Folder…” e abra a pasta abcg
. No canto inferior direito da janela aparecerá uma notificação solicitando se você quer configurar o projeto. Selecione “Yes.”
Ao fazer isso, será feita uma varredura no sistema para identificar os compiladores e toolchains visíveis no PATH
. Uma lista de “kits” encontrados aparecerá na janela “Output” do CMake/Build, como a seguir:
[kit] Found Kit: Clang 11.0.0
[kit] Found Kit: GCC for x86_64-w64-mingw32 10.2.0
Ao final da varredura, selecione o kit “GCC for x86_64-w64-mingw32” versão 10 ou posterior. Ao fazer isso, será iniciado o processo de configuração do CMake. Esse processo gera os arquivos que serão utilizados pelo sistema de construção nativo dentro de um subdiretório build
do projeto.
Se aparecer uma notificação pedindo para configurar o projeto sempre que ele for aberto, Responda “Yes”:
Após o término da configuração do CMake, aparecerá uma outra notificação solicitando permissão para configurar o Intellisense. Responda “Allow.”
Se, além disso, aparecer uma notificação sobre o arquivo compile_commands.json
, como a seguir, responda “Yes” novamente:
compile_commands.json
é um arquivo gerado automaticamente pelo CMake e que contém os comandos de compilação e o caminho de cada unidade de tradução utilizada no projeto. O IntelliSense utiliza as informações desse arquivo para habilitar as referências cruzadas.
A construção dos projetos usando o CMake é feita em duas etapas:
- Configuração: consiste na geração dos scripts do sistema de compilação nativo (por exemplo, arquivos Makefile ou Ninja);
- Construção: consiste no disparo da compilação e ligação usando os scripts gerados na configuração, além da execução de etapas de pré e pós-construção definidas nos scripts dos arquivos
CMakeList.txt
.
Tanto os arquivos da configuração quanto os da construção (binários) são gravados no subdiretório build
.
Geralmente a configuração só precisa ser feita uma vez e depois refeita caso o subdiretório build
tenha sido apagado, ou após a alteração do kit de compilação, ou após a alteração do build type (por exemplo, de Debug
para Release
).
Como indicado na figura abaixo, na barra de status há botões para selecionar o build type e configurar o CMake, selecionar o kit de compilação, e construir a aplicação. A opção de construir já se encarrega de configurar o CMake caso os arquivos de configuração ainda não tenham sido gerados.
Essas opções também estão disponíveis na paleta de comandos do editor, acessada com Ctrl+Shift+P
. Os comandos são:
- “CMake: Select Variant”: para selecionar um build type;
- “CMake: Select a Kit”: para selecionar um kit de compilação;
- “CMake: Configure”: para configurar o CMake usando o kit e o build type atual;
- “CMake: Build”: para construir o projeto.
Os build types permitidos no CMake são:
Debug
para gerar binários não otimizados e com arquivos de símbolos de depuração. Esse é o build type padrão;RelWithDebInfo
para gerar arquivos de símbolos de depuração com binários otimizados;Release
para gerar binários otimizados e favorecer código mais rápido. Essa opção não gera os arquivos de símbolos de depuração;MinSizeRel
, semelhante aoRelease
, mas a otimização tenta gerar binário de menor tamanho.
Para compilar e gerar os binários, tecle F7
ou clique em “Build” na barra de status. O progresso será exibido na janela “Output” do CMake/Build. Se a construção terminar com sucesso, a última linha de texto da janela Output será:
[build] Build finished with exit code 0
Os arquivos gerados na construção ficam armazenados no subdiretório build
, da mesma forma como ocorre na compilação via linha de comando.
Para testar, abra um terminal e execute ./build/bin/helloworld/helloworld
(Linux/macOS) ou .\build\bin\helloworld\helloworld.exe
(Windows).
A configuração do CMake gerada a partir do Visual Studio Code não é necessariamente a mesma gerada usando os scripts de linha de comando: o compilador pode ser diferente, ou o build type pode ser diferente.
Se em algum momento você construir o projeto via linha de comando usando os scripts .sh
ou .bat
e depois quiser construir pelo editor, certifique-se de apagar o subdiretório build
antes de entrar no VS Code. Isso forçará uma nova configuração do CMake e evitará erros de incompatibilidade entre as configurações.
Depurando no Visual Studio Code
Podemos depurar o código com GDB ou LLDB usando a interface do Visual Studio Code.
Após construir o projeto com build type Debug
ou RelWithDebInfo
, selecione a opção “Run” (Ctrl+Shift+D
ou botão na barra de atividades) e então a opção “create a launch.json file” para criar um arquivo launch.json
no subdiretório .vscode
da pasta do projeto:
Em “Select Environment,” selecione “C++ (GDB/LLDB).” Isso criará uma configuração inicial para o arquivo json:
No arquivo launch.json
, modifique o valor da chave program
para apontar para o executável que se deseja depurar. Por exemplo, ${workspaceFolder}/build/bin/helloworld/helloworld
para apontar para o executável do “Hello, World!”
${workspaceFolder}
é uma variável pré-definida do Visual Studio Code que contém o caminho da pasta do projeto. Consulte a documentação para informações sobre outras variáveis disponíveis.
Modifique o valor da chave miDebuggerPath
para o caminho do executável do GDB ou LLDB, ou deixe vazio para usar o padrão do sistema. No Windows, a chave miDebuggerPath
deve conter explicitamente o caminho completo para o GDB, que é C:\msys64\mingw64\bin\gdb.exe
caso o MSYS2 tenha sido instalado em C:\msys64
. No Windows também é necessário configurar o terminal padrão do VS Code para “Command Prompt” no lugar de “PowerShell.”
O exemplo abaixo mostra o conteúdo completo de launch.json
para depurar o “Hello, World!” no Windows.
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/helloworld/helloworld.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"miDebuggerPath": "C:\\msys64\\mingw64\\bin\\gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
Observe que o valor da chave externalConsole
foi modificado para true
para que um terminal de saída seja aberto durante a depuração. Consulte a documentação sobre depuração para informações sobre outras opções e informações gerais sobre como depurar código no editor.
Após modificar o arquivo launch.json
, selecione novamente a opção “Run” na barra de atividades ou tecle F5
para iniciar o programa no modo de depuração.
Reedite o arquivo launch.json
sempre que mudar o nome do executável que se queira depurar.
Compilando para WebAssembly
Podemos compilar as aplicações ABCg para WebAssembly de modo a rodá-las diretamente no navegador. A construção é feita via linha de comando usando o toolchain Emscripten. Acompanhe a seguir como construir o exemplo “Hello, World!” para WebAssembly e abri-lo no navegador:
- Em um terminal (shell ou Prompt de Comando), ative as variáveis de ambiente do Emscripten (script
emsdk_env.sh
/emsdk_env.bat
do SDK). Após isso, o compiladoremcc
deverá estar visível noPATH
; - No diretório
abcg
, executebuild-wasm.sh
(Linux/macOS) oubuild-wasm.bat
(Windows). Isso iniciará a configuração do CMake e a construção dos binários. Os arquivos resultantes serão gerados no subdiretóriopublic
: nesse caso,helloworld.data
(arquivo de dados/assets),helloworld.js
(arquivo JavaScript) ehelloworld.wasm
(binário WebAssembly); - Execute o script
runweb.sh
(Linux/macOS) ourunweb.bat
(Windows) para rodar um servidor web local. O conteúdo depublic
estará disponível em http://localhost:8080/; - Abra a página http://localhost:8080/helloworld.html que chama o script
helloworld.js
recém-criado. A página HTML não faz parte do processo de construção e foi criada previamente.
O resultado será semelhante ao exibido a seguir (uma aplicação mostrando um triângulo colorido e uma caixa de diálogo com alguns controles de interface). A pequena janela de texto abaixo da janela da aplicação mostra o conteúdo do terminal. Nesse caso, são exibidas algumas informações sobre o OpenGL (versão utilizada, fornecedor do driver, etc).
O subdiretório public
contém, além do helloworld.html
:
full_window.html
: para exibir o “Hello, World!” ocupando a janela inteira do navegador;full_window_console.html
: idêntico ao anterior, mas com a sobreposição das mensagens do console na tela.
Nos próximos capítulos veremos como construir novas aplicações usando a ABCg.
Aproveite o restante da primeira semana de aula para se familiarizar com os conceitos do chamado “C++ moderno” (C++11 em diante): ponteiros inteligentes (smart pointers), expressões lambda, variáveis auto e semântica de movimentação (move semantics). Isso facilitará o entendimento do código da ABCg nos próximos capítulos.
Uma referência rápida (cheatsheet) ao C++ moderno está disponível em https://github.com/AnthonyCalandra/modern-cpp-features.
Um excelente livro é o A Tour of C++, de Bjarne Stroustrup. Se não puder ter acesso ao livro, há recursos gratuitos como os sites learncpp.com e tutorialspoint.com.
A documentação da Microsoft sobre C++ é uma opção em português. Há uma referência sobre a linguagem C++ e sobre a biblioteca C++ padrão.
Consulte também o C++ Core Guidelines para ficar a par das boas práticas de programação.
Para uma referência completa da linguagem, consulte cppreference.com. Algumas partes estão traduzidas para o português.