Integrando VSCode, Mingw-w64 y CMake (Parte III)

Artículos de la serie:

Instalación de bibliotecas no-estándar


En el post anterior se analizó la incorporación de extensiones en nuestro editor VSCode para hacer posible la construcción de proyectos mediante CMake.

En este nuevo artículo explicaremos cómo instalar bibliotecas no-estándar en nuestro sistema y su incorporación en proyectos de CMake. Son varias las bibliotecas de uso extendido en la comunidad de C++ cuya inclusión temprana resulta imprescindible en un curso introductorio sobre este lenguaje. Entre ellas, cabría señalar las siguientes:

  • Boost (https://www.boost.org/), colección de bibliotecas de alta calidad que ha proporcionado, históricamente, un campo de desarrollo de soluciones posteriormente adoptadas por el estándar del lenguaje. Incluye, entre otras, Boost.Asio (networking), Boost.Numeric (cálculo numérico) y Boost.Hana (metaprogramación). Para su instalación a través del package manager Pacman de MSYS2, accederemos simplemente a la terminal MSYS2 MSYS desde el botón de inicio de Windows y ejecutaremos el siguiente comando:
pacman -S mingw-w64-ucrt-x86_64-boost
 
  • {fmt}lib (https://fmt.dev/latest/index.html), para el formato de texto con una sintaxis moderna análoga a la ofrecida por el lenguaje Python. Un subconjunto de esta biblioteca se encuentra incorporada en el estándar ISO C++20 (http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p0645r10.html), si bien GCC aún no la implementa. Para su instalación a través de MSYS2, ejecutaremos el siguiente comando:
pacman -S mingw-w64-ucrt-x86_64-fmt
 
  • JSON for Modern C++ (https://github.com/nlohmann/json), para una serialización/deserialización de objetos JSON integrada con la biblioteca estándar del lenguaje. Para su instalación a través de MSYS2, ejecutaremos el siguiente comando:
pacman -S mingw-w64-ucrt-x86_64-nlohmann_json
 
  • Range-v3 (https://github.com/ericniebler/range-v3), para el manejo efectivo de rangos, también incorporada en parte en el estándar C++20 (https://en.cppreference.com/w/cpp/ranges). Puedes acceder a una serie de artículos sobre su funcionalidad a través de este enlace. Para su instalación a través de MSYS2, ejecutaremos el siguiente comando:
pacman -S mingw-w64-ucrt-x86_64-range-v3


Ejemplo de uso


Con el fin de comprobar la correcta instalación de las bibliotecas anteriores, así como la estructura típica que adoptarán los ficheros CMakeLists.txt al emplearlas, acudamos al proyecto básico de CMake de tipo 'Hello, world!' creado en el post anterior. Sustituiremos su código main.cpp por el siguiente programa simple, que introduce en una tabla asociativa (mapa) varios nombres de bandas de Pop/Rock junto a sus integrantes. El programa permitirá al usuario la introducción por terminal de un nombre de grupo a buscar: de encontrarse recogido en el mapa, se mostrará por pantalla la lista de integrantes en orden lexicográfico; en caso contrario, se emitirá un mensaje de error. El programa empleará para ello las bibliotecas {fmt}lib y Range-v3:

   #include <iostream>    #include <map>    #include <string>    #include <vector>    #include <fmt/core.h>    #include <range/v3/algorithm/sort.hpp>    auto main() -> int    {       auto bands = std::map<std::string, std::vector<std::string>>{};       bands["The Beatles"] = {          "Paul McCartney",          "John Lennon",          "Ringo Starr",          "George Harrison"       };       bands["Pink Floyd"] = {          "Nick Mason",          "Syd Barrett",          "Roger Waters",          "Richard Wright",          "David Gilmour",          "Bob Klose"       };       bands["Arctic Monkeys"] = {          "Alex Turner",          "Matt Helders",          "Jamie Cook",          "Nick O'Malley",          "Andy Nicholson",          "Glyn Jones"       };       fmt::print("Search a pop/rock band: ");       auto band_to_search = std::string{};       std::getline(std::cin, band_to_search);       if (auto it = bands.find(band_to_search); it != bands.end()) {          fmt::print("{:_^30}\n", band_to_search);          auto& members = it->second;          ranges::sort(members);          for (auto const& mb : members)             fmt::print("{:^30}\n", mb);       }       else {          fmt::print("Sorry, '{}' is not on the list", band_to_search);       }    }

Por su parte, el fichero CMakeLists.txt del proyecto adoptará ahora la estructura siguiente:

   cmake_minimum_required(VERSION 3.21)    project(prueba       VERSION 0.1.0       DESCRIPTION "Proyecto sencillo de C++ con un solo target"       LANGUAGES CXX    )    add_executable(${PROJECT_NAME} main.cpp)    target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)    set_target_properties(${PROJECT_NAME} PROPERTIES       CXX_EXTENSIONS OFF       RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/debug       RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/release    )    #-------------------------------------------------------------------    # integración de bibliotecas de terceros:    find_package(fmt REQUIRED)    find_package(range-v3 REQUIRED)    target_link_libraries(${PROJECT_NAME} PRIVATE       fmt::fmt       range-v3    )    #-------------------------------------------------------------------    # política de avisos:    if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")       target_compile_options(${PROJECT_NAME} PRIVATE /W3 /WX)    elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")       target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)    endif()    #-------------------------------------------------------------------    # mensajes a emitir en configuración:    message("-- CMake Generator: ${CMAKE_GENERATOR}")    get_target_property(CFEATURES ${PROJECT_NAME} COMPILE_FEATURES)    message("-- Target compile features: ${CFEATURES}")    get_target_property(COPTIONS ${PROJECT_NAME} COMPILE_OPTIONS)    message("-- Target compile options: ${COPTIONS}")

En general, un proyecto podrá contener más de un target, cuyos nombres serán típicamente diferentes a los del proyecto. En un caso tan sencillo como el aquí considerado, sin embargo, en el que el target es único, hemos optado por asignar al ejecutable el mismo nombre que el del proyecto. En el código anterior, la variable ${PROJECT_NAME} propia de CMake será sustituida automáticamente por el string "prueba" (u otro título alternativo que establezcamos a través del comando project()) allá donde aparezca.

Como propiedades del target, hemos solicitado el empleo del último estándar oficial de ISO C++ correspondiente a 2020. El ejecutable se ubicará en la subcarpeta /build/debug/ o /build/release/ según el modo de construcción escogido en la barra de estado inferior de VSCode (ver imagen inferior).


En el caso del compilador GCC, el flag empleado por defecto por CMake durante la compilación en modo debug es simplemente '-g' (depurador sin optimizador), mientras que en modo release se activan los flags '-O3' y '-DNDEBUG' (nivel 3 de optimización y aserciones del depurador eliminadas). Puede comprobarse este punto inspeccionando los flags CMAKE_CXX_FLAGS_DEBUG y CMAKE_CXX_FLAGS_RELEASE en el archivo /build/CMakeCache.txt.

Las opciones de compilación en nuestro archivo CMakeLists.txt distinguen entre los compiladores MS Visual Studio, Clang y GCC, añadiendo los flags con los compiler warnings recomendados en cada caso para el ámbito de la enseñanza (véase http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1389r0.html para más detalles).

Dos aspectos notables del fichero CMakeLists.txt que cabe resaltar son:
  1. No es necesario indicar la extensión del ejecutable generado por el proyecto. El build tool se encargará de añadir aquélla que corresponda a nuestra plataforma.
  2. Una vez instaladas las bibliotecas, el comando find_package hace innecesario indicar explícitamente las direcciones en las que se ubican.

Puedes acceder al siguiente artículo de la serie a través de este enlace.

No hay comentarios:

Publicar un comentario