FireWire 800 hors-service ? peut-être pas !

September 16th, 2010

Sur mon iMac 24″ (acheté en décembre 2007), j’utilise de façon quasi-permanente le connecteur FireWire 800 pour y brancher mon disque dur externe.

Pendant plus d’un an, je n’ai rencontré strictement aucun problèmes. Mais cette semaine, du jour au lendemain, le connecteur FireWire 800 ne semblait plus fonctionner : mon disque dur externe n’était plus reconnu.

J’ai bien sûr fait quelques tests pour savoir d’où pouvait provenir la panne :

  • J’ai changé le câble FireWire : le problème était le même.
  • J’ai connecté un autre disque dur externe FireWire 800 : le problème était le même.
  • J’ai connecté mon disque dur externe sur un port USB : le disque était reconnu !

J’étais donc certain que c’était bien le connecteur FireWire 800 de mon iMac qui posait problème et je commençais à me préparer psychologiquement à devoir procéder à la réparation lorsque je suis tombé sur cet article dans la base de connaissances Apple :

Réinitialisation du contrôleur de gestion du système (SMC)

Tout en bas de l’article, il y a une section à dérouler qui indique spécifiquement pour un iMac comment réinitialiser le SMC. Il y a malheureusement une petite erreur dans cette suite d’étapes. Voici ce que j’ai fait :

  1. Éteindre l’ordinateur.
  2. Débrancher le câble d’alimentation (230V) et attendre 10 secondes.
  3. Rebrancher le câble d’alimentation.
  4. Presser sur le bouton d’enclenchement et laisser appuyer pendant plus de 5 secondes, jusqu’à ce qu’un son long retentisse.
  5. Relâcher le bouton : l’ordinateur démarre.

Et voilà ! Suite à cette manipulation, le connecteur FireWire 800 de mon Mac est à nouveau en service !

Reste à comprendre comment et pourqui, du jour au lendemain, ce connecteur FireWire 800 à décidé de faire des siennes !

Vider complètement la file d’impression

August 25th, 2010

Pour une raison que j’ignore, la file d’impression de mon imprimante contenait un document que je ne parvenais pas à supprimer, même en cliquant sur le bouton Supprimer du gestionnaire d’imprimante.

Ce document n’empêchait pas d’en imprimer d’autres, mais j’ai finalement trouvé le moyen d’effacer complètement une file d’impression. Et c’est tout simple : il suffit de lancer la commande suivante dans le Terminal :

cancel -a

Ajuster la date et l’heure de photos

July 21st, 2010

Lors de sorties ou d’événements en famille ou entre amis, il est fréquent que plusieurs personnes aient leur propre appareil photo. J’aime donc bien récupérer les photos de tout le monde pour les ajouter à ma bibliothèque de photos.

Malheureusement, la plupart des gens oublient de (ou ne savent pas) régler la date et l’heure de leur appareil photo. Ou alors ne mettent pas à jour l’heure suite à un passage à l’heure d’été ou à l’heure d’hiver.

Cela me pose problème car les photos n’apparaissent pas triées correctement dans iPhoto. Heureusement, il existe un moyen très simple pour corriger l’heure et la date d’un groupe de photos.

  1. Sélectionner le groupe de photos dont l’heure et la date doivent être décalées;
  2. Sélectionner Ajuster la date et l'heure... dans le menu Photos;
  3. Indiquer la date et l’heure correctes de la première photo. Le décalage ainsi obtenu sera répercuté sur toutes les autres photos.

    Il est également possible de dire si ces changements doivent être appliqués aux fichiers originaux, ce que je recommande de faire.
  4. Démarrer l’ajustage en cliquant sur Ajuster.

C’est tout simple et iPhoto nous épargne un travail fastidieux si cela devait être fait à la main !

Latex, C++ et les accents

June 10th, 2010

Pour intégrer à un document Latex des morceaux de code en C++, j’utilise le package listings. Celui-ce est certainement le plus connu pour intégrer du code informatique à un document Latex tout en assurant une coloration syntaxique.

J’intègre du code à mes documents Latex principalement de deux façons :

  • en intégrant le morceau de code directement au document Latex, en le plaçant entre les balises \begin{lstlisting} et \end{lstlisting};
  • en important un fichier, avec la balise \lstinputlisting.

La principale difficulté vient de l’encodage des caractères.

Parfois, le code importé provient d’un code écrit sous Windows. L’encodage par défaut du fichier est cp1252. Chaque caractère est encodé sur un octet.

Parfois, le code importé provient d’un code écrit sous Mac OS X. L’encodage par défaut du fichier est applemac. Chaque caractère est encodé sur un octet.

Parfois, le code importé provient d’un code enregistré au format UTF-8. Certains caractères sont encodés sur deux octets.

Enfin, lorsque le code est directement intégré au document Latex, l’encodage est celui du document, en l’occurence UTF-8.

Voici un exemple :

\documentclass[a4paper,11pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern,textcomp}
\usepackage[frenchb]{babel}
\usepackage{listings}
\lstset{
language=C++,
frame=single
}
\begin{document}
Voici du code intégré au document \LaTeX :
\begin{lstlisting}
#include 

int main() {
   std::cout << "Bonjour à tous\n";
   return 0;
}
\end{lstlisting}

Voici du code importé avec un encodage \texttt{Windows} :
\lstinputlisting[inputencoding=cp1252]{main_cp1252.cpp}

Voici du code importé avec un encodage \texttt{Latin1} :
\lstinputlisting[inputencoding=latin1]{main_latin1.cpp}

Voici du code importé avec un encodage \texttt{MacRoman} :
\lstinputlisting[inputencoding=applemac]{main_applemac.cpp}

Voici du code importé avec un encodage \texttt{UTF-8} :
\lstinputlisting{main_utf8.cpp}
\end{document}

Et bien ce code n’est pas compilable ! Le caractère à à la ligne 17 pose problème (c’est un caractère accentué, encodé sur deux octets en UTF-8).

Si, pour aller plus loin, on remplace temporairement ce caractère à par a, la compilation échoue maintenant au moment de l’importation du fichier main_utf8.cpp.

En fait, tout le problème vient du fait que peu importe l’encodage, la paquet listings ne supporte pas les caractères encodés sur plus d’un octet. Si vous souhaitez intégrer du code à un document Latex encodé en UTF-8, il n’y a donc qu’une solution : importer le code depuis un fichier externe encodé avec un format d’un octet par caractère. Dans l’exemple ci-dessus, les formats cp1252, Latin1 et MacRoman fonctionnent très bien.

Finaliser un DVD

May 11th, 2010

J’ai reçu un DVD créé avec un enregistreur de salon. Ce DVD n’avait pas été finalisé.

Lorsque j’essayais de lire ce DVD avec mon Mac, celui-ci n’était pas reconnu. Le problème était identique avec Windows.

J’ai essayé de le finaliser avec mon propre enregistreur de salon, mais sans succès : le DVD n’était pas reconnu.

Finalement, je suis parvenu à le finaliser au moyen de la suite Nero 7, plus particulièrement avec Nero Vision (version utilisée : 4.9.5.7).

Voici comment j’ai procédé.

Tout d’abord, insérer le DVD (qui est détecté comme vide par Windows). Ne rien faire de ce DVD.

Depuis le menu principal de Nero StartSmart, choisir Créer votre propre DVD-Vidéo.

L’application Nero Vision est démarrée. Afficher les outils supplémentaires (bouton More >>) et cliquer sur Finalize Disc.

Normalement, Nero Vision détecte automatiquement la présence du DVD et indique qu’il n’est pas finalisé. Choisir Preserve menus on disc comme option avant la finalisation puis cliquer sur Start.

Au bout de quelques dizaines de secondes, l’opération est terminée et le DVD est parfaitement reconnu.

Cette solution a l’avantage qu’elle fonctionne. Sans elle, je ne sais pas comment j’aurais pu récupérer ce DVD vidéo. Elle présente néanmoins deux défauts majeurs à mes yeux :

  • Nero est un programme commercial (auquel j’ai la chance d’avoir accès par mon école), mais j’aurais préféré une solution gratuite, éventuellement uniquement dédiée à la finalisation de DVDs.
  • Nero ne fonctionne que sous Windows. Je n’ai pas trouvé de solutions fonctionnant avec Mac OS X.

Pub pour le préservatif

April 30th, 2010

Cette pub pour l’usage du préservatif est vraiment chouette !

Des cadres en ligne de commande

April 21st, 2010

Pour avoir de beaux cadres dans un programme Windows en mode console, voici les caractères à utiliser :

// cadre simple
std::cout << "ÚÄÂÄ¿\n";
std::cout << "³ ³ ³\n";
std::cout << "ÃÄÅÄ´\n";
std::cout << "³ ³ ³\n";
std::cout << "ÀÄÁÄÙ\n";

// cadre double
std::cout << "ÉÍËÍ»\n";
std::cout << "º º º\n";
std::cout << "ÌÍÎ͹\n";
std::cout << "º º º\n";
std::cout << "ÈÍÊͼ\n";

Ce qui affichera de zolis cadres !

Attention : cette façon de faire n’est pas portable, car elle dépend complètement du jeu de caractères utilisé par la ligne de commande.

cin et les erreurs

April 21st, 2010

Dans une application console, c’est l’objet cin qui est utilisé pour saisir une valeur au clavier. La valeur saisie est envoyée au moyen de l’opérateur de flux entrant (>>) vers la variable qui recevra la saisie clavier. Le type de la variable déterminera le type de donnée à saisir au clavier.

// Saisie d’un nombre entier
int NombreEntier = 0;
std::cin >> NombreEntier;

// Saisie d’un nombre réel
float NombreReel = 0.;
std::cin >> NombreReel;

// Saisie d’un caractère
char Caractere = 0;
std::cin >> Caractere;

Si l’utilisateur saisi autre chose que ce qui est attendu, cin passe en état d’échec.

Cela signifie que si l’utilisateur saisi une lettre alors que c’est un nombre entier qui est attendu, cin se met en état d’échec (failed state). Voici un exemple très simple qui démontre le problème :

#include <iostream>

int main() {
   int Nombre1 = 12345;
   int Nombre2 = 999;

   std::cout << "Entrez un premier nombre entier : ";
   std::cin >> Nombre1;
   std::cout << "Entrez un second nombre entier : ";
   std::cin >> Nombre2;

   std::cout << "Vous avez saisi les nombres " << Nombre1
             << " et " << Nombre2 << "\n";
   return 0;
}

Si ce qui est saisi ne ressemble pas à un nombre entier, le flux cin passe en état d’échec (ligne 8 ) et toutes les demandes de saisie ultérieures (ligne 10) rendront immédiatement la main au programme sans que rien ne se passe.

Si l’appel à cin est fait au sein d’une boucle (par exemple pour vérifier si le nombre saisi est compris dans un intervalle donné) et que cin passe en état d’échec, le programme tournera en boucle infinie.

#include <iostream>

int main() {
   int Nombre = 0;

   while (true) {
      std::cout << "Entrez un nombre compris entre 1 et 100 : ";
      std::cin >> Nombre;
      if ((Nombre >= 1) && (Nombre <= 100))
         break;
   }
   std::cout << "Vous avez saisi le nombre " << Nombre << "\n";
   return 0;
}

Pour éviter ce problème, il faut tester la valeur de retour du flux cin. Si celle-ci est fausse, c’est qu’une erreur s’est produite et que cin est en état d’échec. Il faut alors rétablir le flux cin en purgeant tout ce qu’il contient.

#include <iostream>
#include <limits>

int main() {
   int Nombre = 0;

   while (true) {
      std::cout << "Entrez un nombre compris entre 1 et 100 : ";
      if (!(std::cin >> Nombre)) {
         // Purge du flux cin
         std::cin.clear();
         std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
      }

      if ((Nombre >= 1) && (Nombre <= 100))
         break;
   }
   std::cout << "Vous avez saisi le nombre " << Nombre << "\n";
   return 0;
}

Voici l’exemple précédent réécrit pour tenir compte de saisies erronées. Les lignes 11 et 12 se chargent de purger le flux cin et de le remettre dans un état valide.

Bien sûr, il serait malvenu d’écrire tout ce code supplémentaire chaque fois que le flux cin est utilisé. Pour éviter ces répétitions, il suffit d’écrire une fonction de saisie d’un nombre entier, qui se charge de traiter l’état d’erreur du flux cin.

Selon les besoins, cette fonction pourra être plus ou moins complexe. Voici une version minimale :

// Fonction de saisie d'un nombre entier au clavier
//@return le nombre entier saisi, ou zéro en cas de saisie invalide.
int getInteger() {
   int Nombre = 0;
   if (!(std::cin >> Nombre)) {
      // Purge du flux cin
      std::cin.clear();
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
   }
   return Nombre;
}

Voici le premier programme de cette annexe réécrit de façon à utiliser la fonction ci-dessus :

#include <iostream>
#include <limits>

// Fonction de saisie d'un nombre entier au clavier
//@return le nombre entier saisi, ou zéro en cas de saisie invalide.
int getInteger() {
   int Nombre = 0;
   if (!(std::cin >> Nombre)) {
      // Purge du flux cin
      std::cin.clear();
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
   }
   return Nombre;
}

// Programme principal
int main() {
   int Nombre1 = 12345;
   int Nombre2 = 999;

   std::cout << "Entrez un premier nombre entier : ";
   Nombre1 = getInteger();
   std::cout << "Entrez un second nombre entier : ";
   Nombre2 = getInteger();

   std::cout << "Vous avez saisi les nombres " << Nombre1
             << " et " << Nombre2 << "\n";
   return 0;
}

Les dates

March 22nd, 2010

Lors de la pratique de son métier, un développeur sera assurément amené à manipuler des dates.

À moins d’une raison très particulière, il ne faudrait pas réinventer la roue et plutôt faire usage des classes prenant déjà en charge la problématique des dates (et de l’heure).

Qt, par exemple, met à disposition les classe QDate et QDateTime.

Mais lorsqu’on apprend la programmation, les exercices sur les dates sont des classiques. Voici ce qui est souvent demandé :

  • déterminer si une année donnée est bissextile ;
  • déterminer le jour de la semaine d’une date donnée ;
  • convertir l’année d’une date en chiffres romains (et vice-versa).

Je reviendrai certainement sur la conversion d’un chiffre arabe en chiffre romain (et vice-versa) dans un autre billet.

Déterminer si une année est bissextile

Pour le calendrier grégorien (celui que l’on utilise de nos jours), une année (le temps que met la Terre pour faire un tour complet autour du Soleil) dure 365.2425 jours.

Avec ce calendrier la méthode pour déterminer si une année donnée est bissextile est la suivante :

  • Une année est bissextile si elle se divise par 4.
  • Une année n’est pas bissextile si elle se divise par 100.
  • Toutefois, si elle se divise par 400, elle est bissextile.
bool isLeapYear(int Year) {
   return (Year % 400 == 0 || (Year % 4 == 0 && Year % 100 != 0) );
}

Mais ce calcul n’est valable qu’avec le calendrier grégorien. Or, celui-ci a été introduit le 15 octobre 1582 par le pape Grégoire XIII.

Pour toutes les années qui précèdent 1582, c’est le calendrier julien qui fait foi. Ce calendrier est basé sur une année de 365.25 jours, ce qui signifie qu’il y a une année bissextile tous les 4 ans.

Notre méthode de calcul peut donc être améliorée :

bool isLeapYear(int Year) {
   if (Year < 1582)
      return abs(Year) % 4 == 0;
   else
      return (Year % 400 == 0 || (Year % 4 == 0 && Year % 100 != 0) );
}

Déterminer le jour de la semaine

La méthode permettant de déterminer quel jour de la semaine est une date donnée est différente selon que la date fait partie du calendrier grégorien ou julien.

Le calendrier julien se termine le 4 octobre 1582. Le calendrier grégorien commence le 15 octobre 1582. Cela signifie que le jour qui suit la date du 4 octobre 1582 est le 15 octobre 1582. Les dates comprises entre le 5 et le 14 octobre 1582 n’existent pas.

prérequis : Jour (1 - 31), Mois (1-12), Année
variables : DécaleAn, MoisDécalé, An, NuméroJour : entiers
début
   DécaleAn <- (14 - Mois) / 12
   An <- Année - DécaleAn
   MoisDécalé <- Mois + 12 * DécaleAn - 2

   si (Année > 1582) ou (Année = 1582 et (Mois > 10) ou (Mois = 10 et Jour >= 15))) alors
      NuméroJour = (Jour + Année + An/4 - An/100 + An/400 + 31 * Mois / 12) modulo 7
   sinon
      si (Année < 1582) ou (Année = 1582 et (Mois < 10 ou (Mois = 10 et Jour <= 4))) alors
         NuméroJour = (5 + Jour + An + An / 4 + ( 31* Mois / 12) modulo 7
      sinon
         NuméroJour = erreur
      fin si
   fin si
fin

NuméroJour vaut 0 pour dimanche, 1 pour lundi, etc.

En C++, la fonction pourrait être la suivante :

int dayOfWeek(int Jour, int Mois, int Annee) {
   int DecaleAn = 14 - Mois / 12;
   int An = Annee - DecaleAn;
   int MoisDecale = Mois + 12 * DecaleAn - 2;
   int NumeroJour = 0;

   if ( (Annee > 1582) ||
        (Annee == 1582 && (Mois > 10) || (Mois == 10 && Jour >= 15))) )
      NumeroJour = (Jour + Annee + An/4 - An/100 + An/400 + 31 * Mois / 12) % 7;
   else if ( (Annee < 1582) ||
                (Année == 1582 && (Mois < 10 || (Mois == 10 && Jour <= 4))) )
      NumeroJour = (5 + Jour + An + An / 4 + ( 31* Mois / 12) % 7;
   else
      NumeroJour = -1; 

   return NumeroJour; // 0 : dimanche, 1 : lundi, etc. -1 : erreur
}

Pourquoi QList::count() retourne un int ?

March 18th, 2010

Les utilisateurs de Qt4 ont peut-être remarqué que la méthode count() ou size() de QList (mais également d’autres classes comme QVector, QMap, etc) retourne un int (donc un entier signé pouvant représenter des valeurs négatives), alors qu’à première vue, un entier non-signé semblerait plus logique.

D’ailleurs, les classes équivalentes de Qt3 retournaient bien un entier non-signé (unsigned int ou uint). Pourquoi ce changement ?

Au premier abord, un entier non-signé semble plus correct qu’un entier signé pour représenter une quantité : une liste ne peut pas contenir un nombre d’éléments négatif. Toutefois à l’usage, il est fréquent de comparer un entier avec le nombre d’élément d’une liste, ce qui provoque à chaque fois un avertissement du compilateur (qui peut être supprimé en faisant un transtypage).

Mais au delà de la stricte application disant qu’une quantité ne peut pas être négative, la raison principale d’utiliser un entier signé est d’éviter des erreurs sournoises.

Par exemple, si count() retourne un entier non-signé, le code suivant fonctionne :

for (uint i = 0; i < count(); ++i)

Mais à l’opposé, le code ci-dessous ne fonctionne pas :

for (uint i = count() - 1; i >= 0; --i)

car si la liste est vide, count() retournera zéro, et au moment d’être décrémenté, il vaudra une valeur positive. C’est un problème sournoi qui ne saute pas aux yeux et qui provoque souvent des boucles quasi-infinies.

Par contre, si count() retourne un entier signé, les deux façons de faire ci-dessous fonctionnent, sans subtilité cachée :

for (int i = 0; i < count(); ++i)
for (int i = count() - 1; i >= 0; --i)