jeudi 23 février 2023

C++20 - std::source_location

J'ai eu l'occasion d'expérimenter avec la structure std::source_location, qui est très bien fichue puisqu'elle s'initialise avec les informations courantes de fonction, de fichier source, et de ligne et de colonne dans le source.

Là où c'est très sympa, c'est qu'il est possible en utilisant un paramètre par défaut d'obtenir dans une fonction les informations de l'appelant ! C'est particulièrement pratique, par exemple, pour implémenter une fonction de log qui ne soit pas une immonde macro.

#include <source_location>
#include <iostream>
#include <string>

void log(std::source_location location = 
         std::source_location::current())
{
    std::cout << location.function_name() << std::endl;
}

void f()
{
    log();
}

template<typename T>
void g()
{
    log();
}

int main()
{
    f();
    g<std::string>();
    g<int>();
    return 0;
}

Et voilà le résultat :

void f()
void g() [with T = std::__cxx11::basic_string<char>]
void g() [with T = int]

Là où ça devient intéressant, c'est quand on veut le combiner avec une fonction de log variadique, de type std::format. Puisque l'on ne peut pas mettre le paramètre par défaut à la fin, car c'est ambigu, il faut ruser, et transformer le premier paramètre, typiquement la chaîne de formattage, en un objet spécial qui prend en premier paramètre la chaîne de formattage et en deuxième le paramètre std::source_location par défaut. Puis combiner tout cela avec du consteval et l'empaqueter dans un std::identity_type_t afin qu'il ne participe pas à la résolution des types variadiques, sinon ça ne marche pas !

Je posterai à l'occasion un exemple un peu plus complet. D'ici là, compilez bien.