Pages

lundi 27 août 2012

Bien surcharger les flux

La surcharge de flux se fait avec les opérateurs << et >>, avec respectivement les classes std::basic_ostream<_CharT, _Traits> et std::basic_istream<_CharT, _Traits>.
Leurs définitions -ainsi que tous les types de flux et buffers de la std- se trouvent dans <iosfwd> (pour ios forward, voir le billet précédent ;)). Inutile d'inclure <ostream> ou <istream> et encore moins <iostream>.

Sur le web beaucoup d'exemples se basent uniquement sur std::ostream et std::istream, mais ce ne sont en fait que des alias.

typedef basic_istream<char> istream;
typedef basic_ostream<char> ostream;

Les exemples montrés avec ces 2 types ne fonctionnent pas lorsque par exemple un std::wcout est utilisé (flux de sortie standard de type wostream = basic_ostream<wchar_t>).

Pour un maximum de compatibilité, il faut utiliser les classes de bases, ce qui donne :

#include <iosfwd>

template<typename _CharT, typename _Traits>
std::basic_ostream<_CharT, _Traits>&
operator<<(std::basic_ostream<_CharT, _Traits>& os, const MyType& a)
{
 return os << ...;
}

template<typename _CharT, typename _Traits>
std::basic_istream<_CharT, _Traits>&
operator>>(std::basic_istream<_CharT, _Traits>& is, MyType& a)
{
 return is >> ...;
}

Dans certaines circonstances, le flux devrait avoir un accès aux membres privés, il faudra donc en faire une fonction amie.

class MyType
{
 //...
 template<typename _CharT, typename _Traits>
 friend std::basic_ostream<_CharT, _Traits>&
 operator<<(std::basic_ostream<_CharT, _Traits>& os, const MyType& a);
};

samedi 25 août 2012

Réduire le temps de compilation avec la déclaration anticipée

Il existe un moyen simple de réduire le temps de compilation d'un programme avec sources et en-têtes séparés : la déclaration anticipée (ou forward declaration).
Cette méthode proposée par certain IDE permet de réduire les dépendances entre en-tête, ce qui réduit le nombre de fichiers analysés. De plus, moins il y a de dépendances moins une modification d'en-tête engendre la re-compilation de source.

La déclaration anticipée ne peut se faire que sur des types indirects comme les pointeurs et références et dont les attributs et méthodes n'ont pas besoin d'être connus dans le fichier.

class B; //forward declaration

class A
{
 B * b;
 //prototype des méthodes
};

lundi 20 août 2012

Comment passer les valeurs d'une tuple à une fonction ?

Par exemple fonction g() retourne une tuple de trois éléments: int, double et string.

std::tuple<int, double, std::string> g();

Une seconde fonction nommée f() attend 3 éléments: string, int et double.

void f(std::string s, int i, double d);

Et nous voulons passer les valeurs de la tuple à f().

auto t = g();
f(std::get<2>(t), std::get<0>(t), std::get<1>(t));

Mais ceci n'est pas suffisant, nous voulons maintenant avoir une fonction qui le fasse pour nous et de façon plus généraliste. On lui indique les 2 fonctions et les index de la tuple à transférer.

template<std::size_t... _indexes>
class parameter_index;

template<typename _TupleGenerator, typename _Function,
std::size_t... _Indexes>
void delegate(const parameter_index<_indexes...>& ,
              _TupleGenerator generator,
              _Function func); 

Maintenant il suffit simplement de donner les index à std::get() pour récupérer toutes les valeurs:

{
 /*return */func(std::get<_indexes>(generator())...);
}

Et c'est tout simple à utiliser:

delegate(parameter_index<2,0,1>(), &g, &f);

Avec un trait qui génère automatiquement le parameter_index et si les valeurs de la tuple sont à transmettre dans le même ordre, il est possible de simplifier en ne mettant pas le parameter_index en paramètre. Un exemple avec tuple_apply tout droit sorti de ma lib falcon.

jeudi 16 août 2012

La récursivité et le mauvais exemple de Fibonacci

Quasiment toute personne ayant suivi un cours sur la récursivité a eu un problème du style : coder la suite de Fibonacci en récursif et en itératif.

Mais partout où j'ai vu une implémentation récursive je suis tombé sur un algorithme non efficace. Pour rappel voici ce qu'on peut trouver.


long long fib(unsigned int n)
{
  if (n == 0)
    return n;
  int a = 0, b = 1, tmp;
  while (--n)
  {
    tmp = a + b;
    a = b;
    b = tmp;
  }
  return b;
}

long long fib_r(unsigned int n)
{
  if (0 == n || 1 == n)
    return n;
  return fib_r(n-1) + fib_r(n-2);
}

Sauf que cet algorithme récursif est pourri, il recalcule sans cesse fib-1 et fib-2 et fait monter la pile là où l'algorithme itératif additionne une variable.
Pour avoir un algorithme récursif équivalent à l'itératif il faut garder le même comportement, c'est à dire garder a et b.


long long fib_r_impl(unsigned int n, long long r, long long rp)
{
  if (0 == n)
    return r;
  return fib_r_impl(n-1, r+rp, r);
}

long long fib_r(unsigned int n)
{
  return fib_r_impl(n,0,1);
}

Cet algo est strictement identique au premier car applique la récursion terminale. Ainsi le code généré par un compilateur (avec les flags d'optimisations) donnera un binaire identique (ou très proche). D'autres langages comme OCaml ou java-script optimise la pile quand il y a une récursion terminale.

mercredi 15 août 2012

Java-script n'est pas réservé à votre navigateur !

Non, java-script permet aussi de créer des commandes Katepart… (cf 90 commandes pour Katepart, qui au passage gravite maintenant autour de 120).

Il est souvent utilisé pour faire des plugins ou des extensions pour divers logiciels. Mais en réalité, avec la bonne API derrière, java-script permet la création de programmes complets et graphiques.

KDE propose la création de plasmoïde (sorte de widget de bureau) en java-script, ruby, python ou C++.En fait l’interpréteur java-script de KDE (nommé tout naturellement kjs) permet de communiquer avec les API Qt/KDE pour bénéficier de toute la panoplie des widgets graphiques et de bien d'autres composants.

Kjs n'est pas le seul, rhino qui n'est autre que l’interpréteur de Mozilla permet de communiquer directement avec les classes java. De plus, rhino est actuellement le seul à supporter la version 1.8 de java-script. Les autres interpréteurs ne rajoutent que des objets ou méthodes. Tous les ajouts syntaxiques sur le langage comme la notation lambda, les générateurs d'expression, l'array compréhension et d'autres sont inexistants (nouveauté de js 1.8 sur developer.mozilla.org).

Il existe aussi la plate-forme Node.js de plus en plus répandu sur les serveurs web.

Et MongoDB, une base de donnée NoSQL utilise java-script comme langage de requête.

Tout ça pour dire que java-script est utilisé au-delà du navigateur, contrairement à ce qu'en pensent certains.

mardi 14 août 2012

Je reviens (le retour de ma seconde absence)

Après une grande réflexion de plusieurs mois et un silence radio total sur ce blog ‑qui n'a jamais vraiment été vivant‑, j'ai décidé de le mettre à jour !

Parler de façon déchronologique de toutes mes découvertes dans le monde riche des langages de programmation.

En plus je viens de voir 2 brouillons non publiés sur le C++.

C'est Denis qui va être content (imagine que je fais ça pour toi :)).

Mais dans toute ma gentillesse, le prochain message ne l’intéressera sans doute pas.