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?
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");
}
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.
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);
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());
// ...
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);
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));
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));