web-dev-qa-db-fra.com

Comment utiliser source_location dans une fonction de modèle variadic?

La fonctionnalité C++ 20 std::source_location est utilisé pour capturer des informations sur le contexte dans lequel une fonction est appelée. Lorsque j'essaie de l'utiliser avec une fonction de modèle variadic, j'ai rencontré un problème: je ne vois pas où placer le source_location paramètre.

Ce qui suit ne fonctionne pas car les paramètres variadiques doivent être à la fin:

// doesn't work
template <typename... Args>
void debug(Args&&... args,
           const std::source_location& loc = std::source_location::current());

Ce qui suit ne fonctionne pas non plus car l'appelant sera foutu par le paramètre inséré entre les deux:

// doesn't work either, because ...
template <typename... Args>
void debug(const std::source_location& loc = std::source_location::current(),
           Args&&... args);

// the caller will get confused
debug(42); // error: cannot convert 42 to std::source_location

J'ai été informé dans un commentaire que std::source_location fonctionne parfaitement avec les modèles variadic, mais j'ai du mal à comprendre comment. Comment puis-je utiliser std::source_location avec des fonctions de modèle variadic?

39
L. F.

Le premier formulaire peut être fait fonctionner, en ajoutant un guide de déduction:

template <typename... Ts>
struct debug
{    
    debug(Ts&&... ts, const std::source_location& loc = std::source_location::current());
};

template <typename... Ts>
debug(Ts&&...) -> debug<Ts...>;

Tester:

int main()
{
    debug(5, 'A', 3.14f, "foo");
}

[~ # ~] démo [~ # ~]

35
Piotr Skotnicki

Mettez simplement vos arguments dans un Tuple, aucune macro n'est nécessaire.

#include <source_location>
#include <Tuple>

template <typename... Args>
void debug(
    std::Tuple<Args...> args,
    const std::source_location& loc = std::source_location::current())
{
    std::cout 
        << "debug() called from source location "
        << loc.file_name() << ":" << loc.line()  << '\n';
}

Et cela fonctionne*.

Techniquement, vous pourriez simplement écrire:

template <typename T>
void debug(
    T arg, 
    const std::source_location& loc = std::source_location::current())
{
    std::cout 
        << "debug() called from source location "
        << loc.file_name() << ":" << loc.line()  << '\n';
}

mais alors vous devrez probablement sauter à travers quelques cerceaux pour obtenir les types d'arguments.


* Dans l'exemple lié, j'utilise <experimental/source_location> car c'est ce que les compilateurs acceptent en ce moment. De plus, j'ai ajouté du code pour imprimer l'argument Tuple.

8
einpoklum
template <typename... Args>
void debug(Args&&... args,
           const std::source_location& loc = std::source_location::current());

"fonctionne", mais nécessite de spécifier des arguments de modèle car ils ne sont pas déductibles car ils ne sont pas les derniers:

debug<int>(42);

Démo

Les alternatives possibles (pas parfaites) comprennent:

  • utiliser des surcharges avec une limite codée en dur (ancienne façon possible de "gérer" les variades):

    // 0 arguments
    void debug(const std::source_location& loc = std::source_location::current());
    
    // 1 argument
    template <typename T0>
    void debug(T0&& t0,
               const std::source_location& loc = std::source_location::current());
    
    // 2 arguments
    template <typename T0, typename T1>
    void debug(T0&& t0, T1&& t1,
               const std::source_location& loc = std::source_location::current());
    
    // ...
    

    Démo

  • mettre source_location en première position, sans défaut:

    template <typename... Args>
    void debug(const std::source_location& loc, Args&&... args);
    

    et

    debug(std::source_location::current(), 42);
    

    Démo

  • de la même manière que les surcharges, mais utilisez simplement Tuple comme groupe

    template <typename Tuple>
    void debug(Tuple&& t,
               const std::source_location& loc = std::source_location::current());
    

    ou

    template <typename ... Ts>
    void debug(const std::Tuple<Ts...>& t,
               const std::source_location& loc = std::source_location::current());
    

    avec usage

    debug(std::make_Tuple(42));
    

    Démo

4
Jarod42

Pas une bonne solution mais ... qu'en est-il de placer les arguments variadiques dans un std::Tuple?

Je veux dire ... quelque chose comme

template <typename... Args>
void debug (std::Tuple<Args...> && t_args,
            std::source_location const & loc = std::source_location::current());

Malheureusement, de cette façon, vous devez appeler explicitement std::make_Tuple l'appeler

debug(std::make_Tuple(1, 2l, 3ll));
4
max66