Pages

dimanche 22 décembre 2013

Parcourir les arguments d'une fonction variadique

À l'approche de Noël et du déballage de cadeaux, faisons un tour sur le déballage des paramètres variadiques.

La méthode habituelle pour utiliser chaque paramètre est la récursion jusqu'à plus d'argument ou jusqu'à un nombre défini, généralement 1.

Quelque chose comme ça:

#include <iostream>
 
void f1()
{}
 
template<class T, class... Args>
void f1(const T& first, const Args&... others)
{
  std::cout << first << '\n';
  f1(others...);
}
 
 
template<class T>
void f2(const T& first)
{
  std::cout << first << '\n'; 
}
 
template<class T, class... Args>
void f2(const T& first, const Args&... others)
{
  std::cout << first << ", ";
  f2(others...);
}
 
 
int main()
{
 f1(1, 2.4, "plop");
 f2(1, 2.4, "plop");
}

Ce qui a affiche:

1
2.4
plop
1, 2.4, plop

Il existe cependant une autre façon de faire qui n'utilise pas la récursivité. J'ai découvert cette méthode sur le forum de openclassroms (ce sujet, 10ème message), elle est très astucieuse.

Implémentée sous la forme d'une macro cela donne:

#define UNPACK(...) (void(::std::initializer_list<char>{(void((__VA_ARGS__)), '\0')...}))
  • Le premier void permet d'ignorer l'initializer_list créé.
  • (void((__VA_ARGS__)), '\0') évalue l'ensemble de l'expression, ignore le résulat (grâce au void) et retourne un caractère dans l'initalizer_list.
  • Au final l'expression est dépaquetée et l'initializer_list est rempli de 0.

Avec cette macro les 2 fonctions précédentes deviennent:

#include <initializer_list>

#define UNPACK(...) (void(::std::initializer_list<char>{(void((__VA_ARGS__)), '\0')...}))

template<class... Args>
void f1(const Args&... args)
{
  UNPACK(std::cout << args << '\n');
}

template<class T, class... Args>
void f2(const T& first, const Args&... others)
{
  std::cout << first;
  UNPACK(std::cout << ", " << others);
  std::cout << '\n';
}

Ceci simplifie considérablement l'écriture :).

Évidemment, cette macro se trouve depuis dans falcon: CPP1X_UNPACK.