Quelques raccourcis :

420KH2 – Systèmes temps réel (STR)

Ceci est un petit site de support pour le cours 420-KH2-LG – Systèmes temps réel. Notez que plusieurs liens divers sont mis à votre disposition; parmi ceux-ci, soyez particulièrement attentives et attentifs à ceux portant :

Documents sous forme électronique

Cliquez sur cette cible pour le plan de cours, sous forme électronique

Détail des séances en classe

Index des séances théoriques
T00 T01 T02 T03 T04 T05 T06 T07 T08 T09 T10 T11 T12 T13 T14
Date Séance Contenu

24 janvier

T00

Au menu :

À titre de petit bonbon, s'ajoutera un mot sur les sympathiques et Ô combien expressives expressions λ.

Les cours T00 et L00 se déborderont un peu l'un sur l'autre, question de nous permettre de démarrer la session...

25 janvier

L00

Pour vous pratiquer, prenez le code suivant :

#include <iostream>
#include <algorithm>
#include <string>
#include <locale>
using namespace std;
void rendre_majuscule(string &s, const locale &loc) {
   for(string::size_type i = 0; i < s.size(); ++i)
      s[i] = toupper(s[i], loc);
}
int main() {
   enum { NMOTS = 10 };
   string mots[NMOTS];
   for (int i = 0; i < NMOTS; ++i)
      cin >> mots[i];
   const auto &loc = locale{""}; // la culture sur cet ordinateur
   for(int i = 0; i < NMOTS; ++i)
      rendre_majuscule(mots[i], loc);
   for(int i = 0; i < NMOTS; ++i)
      cout << mots[i] << endl;
}

Et modifiez-le pour qu'il :

  • Utilise un std::vector<std::string> plutôt qu'un tableau de std::string
  • Lise autant de mots que l'usager ait choisi d'en entrer. Dans vos tests, vous pourrez provoquer une erreur de lecture à la console en appuyant CTRL+Z quand vous aurez décidé que vous en avez assez. Un mot est ici une séquence de caractères délimitée à la fin par au moins un « blanc » (espace, tabulation, saut de ligne, etc.)
  • Transforme chacun des mots en son équivalent en majuscules, cette fois à l'aide d'un std::for_each() et d'un foncteur. Le constructeur du foncteur vous permettra de capturer le std::locale utilisé comme second paramètre à std::toupper()
  • Affichera chacun des mots sur une ligne distincte à l'aide de std::for_each() et d'un foncteur Afficher, inspiré de celui proposé en classe à la séance T00

Si le coeur vous en dit, essayez de faire la même chose avec des expressions λ, mais conservez une copie de sauvegarde de la version utilisant des foncteurs pour fins de discussion en classe.

Solution avec foncteurs :

#include <iostream>
#include <algorithm>
#include <string>
#include <locale>
#include <vector>
void rendre_majuscule(std::string &s, const std::locale &loc)
{
   using std::string;
   using std::toupper;
   for(string::size_type i = 0; i < s.size(); ++i)
      s[i] = toupper(s[i], loc);
}
class Afficher
{
   std::ostream &os_;
public:
   Afficher(std::ostream &os)
      : os_(os)
   {
   }
   void operator()(const std::string &s)
      { os_ << s << std::endl; }
};
class RendreMajuscule
{
   const std::locale &loc_;
public:
   RendreMajuscule(const std::locale &loc)
      : loc_(loc)
   {
   }
   void operator()(std::string &s)
      { rendre_majuscule(s, loc_); }
};
int main()
{
   using std::cin;
   using std::cout;
   using std::endl;
   using std::locale;
   using std::string;
   using std::vector;
   using std::for_each;
   vector<string> mots;
   for (string mot; cin >> mot; mots.push_back(mot))
      ;
   const locale &loc = locale(""); // la culture sur cet ordinateur
   for_each(mots.begin(), mots.end(), RendreMajuscule(loc));
   for_each(mots.begin(), mots.end(), Afficher(cout));
}

Une solution avec expressions λ serait la suivante. Notez qu'il est possible que Visual Studio 2010 plante à la compilation avec ce code, car il a parfois de la difficulté à gérer les λ; cependant, le code est correct – testez-le avec un compilateur plus récent, comme par exemple celui livré avec Visual Studio 2012 ou une version récente de g++ ou de Clang :

#include <iostream>
#include <algorithm>
#include <string>
#include <locale>
#include <vector>
void rendre_majuscule(std::string &s, const std::locale &loc)
{
   using std::string;
   using std::toupper;
   for(string::size_type i = 0; i < s.size(); ++i)
      s[i] = toupper(s[i], loc);
}
int main()
{
   using std::cin;
   using std::cout;
   using std::endl;
   using std::locale;
   using std::string;
   using std::vector;
   using std::for_each;
   vector<string> mots;
   for (string mot; cin >> mot; mots.push_back(mot))
      ;
   const locale &loc = locale(""); // la culture sur cet ordinateur
   for_each(mots.begin(), mots.end(), [&](string &s) {
      rendre_majuscule(s, loc);
   });
   for_each(mots.begin(), mots.end(), [&](const string &s) {
      cout << s << endl;
   });
}

Autre exercice pertinent : modifiez la fonction rendre_majuscule() pour qu'elle opère sur des itérateurs plutôt que sur des indices.

Solution possible :

void rendre_majuscule(std::string &s, const std::locale &loc) {
   using namespace std;
   for(string::iterator i = s.begin(); i != s.end(); ++i)
      *i = toupper(*i, loc);
}

Autre solution possible (vive auto) :

void rendre_majuscule(std::string &s, const std::locale &loc) {
   using namespace std;
   for(auto i = s.begin(); i != s.end(); ++i)
      *i = toupper(*i, loc);
}

Autre solution possible, si vous incluez <iterator> :

void rendre_majuscule(std::string &s, const std::locale &loc) {
   using namespace std;
   for(auto i = begin(s); i != end(s); ++i)
      *i = toupper(*i, loc);
}

Pour pratiquer un peu, vous êtes invité(e)s à faire les exercices suivants. N'utilisez pas les algorithmes de l'en-tête <algorithm> pour arriver à vos fins (nous voulons nous pratiquer, après tout) :

  • Écrivez un algorithme nommé inverser_elements() qui prend en paramètre une séquence à demi ouverte et inverse l'ordre de ses éléments. Montrez par un programme de test que votre fonction opère correctement sur un tableau de std::string, un vecteur de int et une liste de double, et ce, que le nombre d'éléments soit nul, un, pair ou impair
  • Écrivez un algorithme nommé compter_occurrences() qui prend en paramètre une séquence à demi ouverte et une instance d'un type T donné, qu'on peut comparer à l'aide de == avec les éléments de la séquence, et qui retourne le nombre d'occurrences de cette instance dans la séquence. Montrez par un programme de test que votre fonction opère correctement sur un tableau de std::string, un vecteur de int et une liste de char, et ce, que le nombre d'éléments soit nul, un ou plus, que la valeur recherchée soit dans la séquence ou non. Notez que je n'ai pas demandé d'utiliser une liste de double dans ce cas-ci... Comprenez-vous pourquoi?
  • Écrivez un algorithme nommé compter_si() qui prend en paramètre une séquence à demi ouverte et un prédicat (un prédicat est une opération booléenne) applicable à chaque élément de la séquence, et qui retourne le nombre d'instances dans la séquence pour lesquelles le prédicat s'avère vrai. Montrez par un programme de test que votre fonction opère correctement sur un tableau de std::string, un vecteur de int et une liste de double, et ce, que le nombre d'éléments soit nul, un ou plus, qu'il y ait ou non des instances pour lesquelles le prédicat est vrai. Écrivez au moins deux prédicats distincts (un foncteur et une fonction)

Par la suite (ne trichez pas!), essayez de voir si ces algorithmes sont codés dans <algorithm> (respectivement sous les noms reverse(), count() et count_if(), tous dans l'espace nommé std), et comparez vos implémentations avec celles que vous y trouverez.

Pour réaliser les exercices proposés ici, il vous faudra utiliser entre autres les types std::vector et std::list, que vous trouverez dans les en-têtes standards <vector> et <list> respectivement :

  • Le type vector représente une sorte de tableau dynamique, très efficace pour accéder à un élément particulier ou pour ajouter ou enlever un élément à la fin, mais moins efficace pour insérer ou retirer des éléments à d'autres positions.
  • Le type list représente une liste doublement chaînée; les opérations sur elle sont en général plus lentes que sur vector, mais les opérations d'insertion et de suppression à un endroit autre qu'à la toute fin y sont beaucoup plus rapides.

Pour en savoir plus sur les conteneurs standards et sur les algorithmes standards, jetez un coup d'oeil à cet article.

31 janvier

T01

Au menu :

  • Petit récapitulatif sur les exercices de la séance L00 (on examine des solutions, et les raisons derrière les subtilités de chaque problème)

D'ailleurs, pour ces problèmes, des solutions possibles suivent (nous les expliquerons en classe, mais vous pouvez préparer vos questions en attendant). Important : c'est votre premier contact avec cette manière de programmer et de résoudre les problèmes, alors ne vous en faites pas si vous avez eu des ennuis – c'est avec la pratique que l'on parvient à intégrer ces techniques!

Solution possible pour inverser_elements() :

template <class It>
   void inverser_elements(It debut, It fin) {
      using std::swap; // inclure <utility> pour ceci
      if (debut == fin) return; // séquence vide
      --fin; // car la fin est exclue de la séquence
      while (debut != fin) {
         swap(*debut, *fin);
         ++debut;
         if (debut != fin) // au cas où avancer debut aurait conclu le tout
            --fin;
      }
   }

Remarquez l'alternative (le if) dans la répétitive : puisque nous déplaçons deux itérateurs à chaque itération, il faut réaliser deux tests pour éviter de déborder de la séquence.

Solution plus compacte grâce à la boucle for, mais équivalente :

template <class It>
   void inverser_elements(It debut, It fin) {
      using std::swap;
      if (debut == fin) return;
      for(--fin; debut != fin; ) {
         swap(*debut, *fin);
         if (++debut != fin)
            --fin;
      }
   }

Tests possibles :

// ...
int vals[] { 2,3,5,7,11 };
inverser_elements(begin(vals), end(vals));
vector<string> v{ "!", "prof", "mon", "J'aime" };
inverser_elements(begin(v), end(v));
list<float> lst; // vide
inverser_elements(begin(lst), end(lst));

Solution possible pour compter_occurrences() :

template <class It, class T>
   int compter_occurrences(It debut, It fin, T elem) {
      int n = 0;
      for(; debut != fin; ++debut)
         if (*debut == elem)
            ++n;
      return n;
   }

Tests possibles :

// ...
int vals[] { 2,3,5,7,7,11 };
cout << compter_occurrences(begin(vals), end(vals), 5) <<endl; // affichera 1
cout << compter_occurrences(begin(vals), end(vals), 7) <<endl; // affichera 2
cout << compter_occurrences(begin(vals), end(vals), 33) <<endl; // affichera 0

Solution possible pour compter_si() :

template <class It, class Pred>
   int compter_si(It debut, It fin, Pred pred) {
      int n = 0;
      for(; debut != fin; ++debut)
         if (pred(*debut))
            ++n;
      return n;
   }

Le troisième paramètre est un prédicat (une fonction booléenne qui prend en paramètre un élément de la séquence).

Tests possibles :

// ...
int vals[] { 2,3,5,7,11 };
cout << compter_si(begin(vals), end(vals), [](int n) { return n % 2 == 0; }) <<endl; // affichera 1
cout << compter_si(begin(vals), end(vals), [](int n) { return n % 2 != 0; }) <<endl; // affichera 4
cout << compter_si(begin(vals), end(vals), [](int n) { return n < 0; }) <<endl; // affichera 0

Avec une λ, on peut exprimer compter_occurrences() en termes de compter_si(), qui est plus général :

template <class It, class T>
   int compter_occurrences(It debut, It fin, T elem) {
      return compter_si(debut, fin, [elem](auto val) {
         return val == elem;
      });
   }

Le TP00 est à remettre au plus tard à la fin de la séance L02.

1er février

L01

Au menu :

7 février

T02

Au menu :

  • Points techniques divers, pour vous aider à résoudre le TP00 :
    • design d'un thread (rôle, enjeux, responsabilités, durée de vie, etc.)
    • comprendre le fonctionnement d'un vector dans un contexte de temps réel
  • Travail sur le TP00

8 février

L02

Au menu :

  • Q01
  • Points techniques divers, pour vous aider à résoudre le TP00 :
  • Travail sur le TP00

À la demande générale, la remise du TP00 est repoussée au début de la séance T03

14 février

T03

Au menu :

Le TP00 doit être remis en format imprimé à votre chic prof au début de cette séance de cours.

C'est aujourd'hui la date limite pour annuler un cours... mais j'espère vous garder avec moi 

15 février

L03

Au menu :

  • Exercice formatif. Par équipe de deux, décrivez une classe file_circulaire représentant une file circulaire de int dont la capacité est fixée à la construction
  • Votre classe doit au minimum offrir les services suivants (vous pouvez faire plus si vous le jugez pertinent) :
    • un constructeur par défaut, créant une file vide d'une capacité par défaut (disons 1024)
    • un constructeur paramétrique, acceptant une capacité en paramètre et créant une file vide ayant cette capacité
    • une méthode size() retournant le nombre d'éléments qui s'y trouvent
    • une méthode capacity() retournant sa capacité
    • une méthode empty() retournant true seulement si elle est vide
    • une méthode full() retournant true seulement si elle est pleine
    • une méthode add() permettant d'ajouter un élément dans la file au point d'insertion courant
    • une méthode remove() permettant d'enlever un élément de la file au point de consommation courant
    • une méthode peek() retournant l'élément qui sera retiré lors du prochain appel à remove()
  • Dans le design de votre classe, cherchez à être le plus efficaces possibles, en termes de temps et d'espace. Prenez aussi soin de réfléchir aux questions suivantes :
    • les choix d'implémentation que vous avez faits sont-ils pertinents? Auriez-vous pu faire mieux? Il est normal de faire des choix, qui favorisent certaines opérations plutôt que d'autres, mais il est sain de se demander si nos choix furent les meilleurs dans les circonstances
    • les types des méthodes et de leurs paramètres sont-ils bien choisis?
    • l'interface de la classe est-elle cohérente (noms, ordre des paramètres, façons de faire, etc.)?
    • quel devrait être le comportement de la méthode add() si la file est pleine?
    • quel devrait être le comportement de remove() si la file est vide?
    • quel devrait être le comportement de peek() si la file est vide?
    • si nous souhaitions partager cette file entre deux threads (un producteur, un consommateur), votre implémentation serait-elle appropriée?

Un code de test possible serait le suivant (à adapter en fonction des noms et des pratiques de gestion d'erreurs que vous aurez choisi d'utiliser) :

#include "file_circulaire.h"
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
   file_circulaire ze_file;
   assert(ze_file.empty());
   assert(ze_file.capacity() == file_circulaire::DEFAULT_CAPACITY);
   assert(ze_file.size() == 0);
   int vals[] = { 2, 3, 5, 7, 11 };
   for (auto n : vals) ze_file.add(n);
   assert(!ze_file.empty());
   assert(ze_file.capacity() == file_circulaire::DEFAULT_CAPACITY);
   assert(ze_file.size() == end(vals) - begin(vals));
   ze_file = file_circulaire{ 10 };
   for (int i = 0; i < 9; ++i)
      ze_file.add(i + 1);
   assert(!ze_file.empty());
   assert(ze_file.capacity() == 10);
   assert(ze_file.size() == 9);
   try {
      ze_file.add(10);
      cerr << "Ajout dans une file pleine (pas suppose se produire)" << endl;
   } catch (capacity_overflow &) {
      cout << "file pleine (correct)" << endl;
   }
   while (!ze_file.empty()) {
      cout << ze_file.peek() << endl;
      ze_file.remove();
   }
   try {
      ze_file.peek();
      cerr << "Lecture dans une file vide (pas suppose se produire)" << endl;
   }  catch (capacity_underflow &) {
      cout << "file vide (correct)" << endl;
   }
   try {
      ze_file.remove();
      cerr << "Suppression dans une file vide (pas suppose se produire)" << endl;
   } catch (capacity_underflow &) {
      cout << "file vide (correct)" << endl;
   }
}

Avec mon implémentation, j'obtiens ceci :

file pleine (correct)
1
2
3
4
5
6
7
8
9
file vide (correct)
file vide (correct)

Mon implémentation personnelle ressemble à :

#ifndef FILE_CIRCULAIRE_H
#define FILE_CIRCULAIRE_H
#include <vector>
class capacity_overflow {};
class capacity_underflow {};
class file_circulaire {
public:
   using value_type = int;
   using reference = value_type&;
   using const_reference = const value_type&;
private:
   using container_type = std::vector<value_type>;
   container_type buf;
public:
   using size_type = container_type::size_type;
private:
   size_type prod_pt = {},
             cons_pt = {};
   static size_type next(size_type n, const container_type &c) {
      return (n + 1) % c.size();
   }
public:
   enum : size_type { DEFAULT_CAPACITY = 1024 };
   file_circulaire(size_type cap = DEFAULT_CAPACITY) : buf(cap, value_type{}) {
   }
   size_type size() const noexcept {
      return cons_pt <= prod_pt ? prod_pt - cons_pt : prod_pt + capacity() - cons_pt;
   }
   size_type capacity() const noexcept {
      return buf.capacity();
   }
   bool empty() const noexcept {
      return cons_pt == prod_pt;
   }
   bool full() const noexcept {
      return next(prod_pt, buf) == cons_pt;
   }
   void add(const_reference val) {
      if (full()) throw capacity_overflow{};
      buf[prod_pt] = val;
      prod_pt = next(prod_pt, buf);
   }
   void remove() {
      if (empty()) throw capacity_underflow{};
      cons_pt = next(cons_pt, buf);
   }
   reference peek() {
      if (empty()) throw capacity_underflow{};
      return buf[cons_pt];
   }
   const_reference peek() const {
      if (empty()) throw capacity_underflow{};
      return buf[cons_pt];
   }
};
#endif

21 février

T04

Au menu :

  • Examen d'un tableau générique naïf (première approche)

22 février

L04

Au menu :

Pendant la semaine de mise à niveau, alors que je serai au loin mais enfoui dans une salle avec air climatisé et café à profusion, à essayer de ficeler C++ 17 (j'ai hâte!), je vous propose de vous adonner à une petite activité formative (donc qui ne sera pas notée), pour ne pas perdre la main. Merci à Pierre-Luc Brault pour avoir suggéré cette approche.

L'activité proposée va comme suit :

  • Retravailler la file_circulaire sur laquelle vous avez travaillé à la séance L03 (basez-vous sur la mienne ou sur la vôtre, à votre convenance) et en faire une classe générique (une file_circulaire<T>)
  • Examinez votre implémentation, une fois celle-ci fonctionnelle. Comment se comporte-t-elle si une copie du type T lève une exception? La file_circulaire<T> devient-elle corrompue? Sa taille est-elle toujours correcte? Perdez-vous des données?
  • Avec votre implémentation (et la réponse dépendra vraiment de votre implémentation), est-il pertinent d'implémenter :
  • Pour chacune de ces opérations, si vous estimez sage de les implémenter, alors faites-le

28 février

Semaine de mise à niveau (cours suspendus). Je serai pour ma part à Kona pour la rencontre du WG21. Vous pouvez suivre mes aventures sur ../../../Sujets/Orthogonal/wg21-2017-Kona.html si vous le souhaitez

1er mars

Semaine de mise à niveau (cours suspendus). Je serai pour ma part à Kona pour la rencontre du WG21. Vous pouvez suivre mes aventures sur ../../../Sujets/Orthogonal/wg21-2017-Kona.html si vous le souhaitez

7 mars

T05

Au menu :

  • Début d'un examen des mécanismes de gestion de la mémoire tels que new, delete, new[] et delete[]
  • Nous n'examinerons pas que ceux-ci, alors préparez-vous à une aventure étrange dans la programmation près du matériel

Les cas de surcharge de new, new[], delete et delete[] examinés aujourd'hui incluent :

void *operator new(size_t); // p.ex.: X * p = new X(3, "J'aime mon prof");
void *operator new[](size_t); // p.ex.: X * q = new X[20];
void operator delete(void *); // p.ex.: delete p;
void operator delete[](void *); // p.ex.: delete [] q;

Ce sont les versions « de base », fonctions globales. Quand on remplace ça, ça a un impact sur presque tout alors faut y aller avec prudence.

Nous avons aussi examiné l'allocation positionnelle, ou Placement New, qui permet de placer un objet à un endroit de notre choix en mémoire :

class X { /* ... */ };
X f(); // retournera un X si on l'appelle
void g() {
   alignas(X) char buf[sizeof(X)] {}; // tampon plein de zéros, assez gros pour contenir un X
   X *p = new (static_cast<void*>(&buf[0])) X{ f() }; // créer une copie de ce que retourne f() dans buf
   // ...utiliser *p ...
   p->~X(); // détruire le X en question sans libérer la mémoire sous-jacente
}

Nous n'avons pas terminé de couvrir le sujet...

8 mars

L05

Au menu :

14 mars

T06

Au menu :

Le TP01 est à remettre au plus tard à la fin de la séance L08.

15 mars

L06

Au menu :

  • Cours sous forme de tutorat en-ligne, pour cause de conditions routières dangereuses (on vous veut en santé, et en vie)
  • Travail sur le TP01

21 mars

T07

Au menu :

22 mars

L07

Au menu :

  • Travail sur le TP01

28 mars

T08

Au menu :

29 mars

L08

Au menu :

Le TP01 doit être remis en format imprimé à votre chic prof à la fin de cette séance de cours. Nous avons choisi de déplacer la remise à la séance T09.

4 avril

T09

Au menu :

Le TP01 doit être remis en format imprimé à votre chic prof à la fin de cette séance de cours.

5 avril

L09

Au menu :

  • Travail sur le TP02

11 avril

T10

Au menu :

12 avril

L10

Au menu :

18 avril

T11

Au menu :

19 avril

L11

Au menu :

Remise du TP02 doit être remis en format imprimé à votre chic prof au début du cours

25 avril

T12

Au menu :

26 avril

L12

Au menu :

Je vais devoir quitter aux alentours de 17 h, mais c'est pour aller remettre une bourse à un de vos collègues, illustre finissant en informatique industrielle. Je pense que c'est une bonne cause 

2 mai

T13

Au menu :

  • Q07
  • Échange sur votre travail intégrateur dans les autres cours, pour définir ce que sera l'A.S. H2017
  • Temps pour oeuvrer sur sur votre travail intégrateur

3 mai

L13

Au menu :

  • Présentation de l'A.S. H2017
  • Temps pour oeuvrer sur sur votre travail intégrateur, incluant l'A.S. H2017

9 mai

Journée d'examen pour les cours de français de la formation générale (cours suspendus)

10 mai

L14

Au menu :

À titre de Q08 :

  • Q08.0 Pour un point, dans un système d'exploitation temps réel, comment nomme-t-on une situation où un processus de forte priorité est bloqué par le fait qu'un processus de plus faible priorité possède une ressource dont il a besoin (p. ex. : un mutex) mais ne parvient pas à la libérer du fait que le processus à haute priorité est toujours celui qui obtient le droit de s'exécuter?
  • Q08.1 Pour un point, comment se nomme la solution que peut offrir un système d'exploitation temps réel à une telle situation d'interblocage?
  • Q08.2 Pour deux points, expliquez brièvement la solution que vous avez nommée à Q08.1
  • Q08.3 Pour un point, nous avons vu que certains mécanismes de communication de QNX comme MsgSend() sont bloquants (en effet, MsgSend() bloque jusqu'à ce que l'homologue fasse MsgReply()). Expliquez brièvement un avantage d'offrir un tel mécanisme de communication bloquant dans un système d'exploitation temps réel

Envoyez à votre chic prof un message contenant vos réponses à Q08 par Colnet. Vos réponses doivent être reçues au plus tard à 23 h 59 le 10 mai 2017 (donc ce soir).

16 mai

T14

Au menu : à déterminer

Consignes des travaux pratiques

Les consignes des travaux pratiques sont telles qu'indiqué ci-dessous.

Consignes Document d'accompagnement

TP00

420KH2--TP00.PDF

Archive de démarrage : TP00--Consommateur.zip

TP01

420KH2--TP01.pdf

TP02

Programme ppm_to_rle.exe (il génère à la fois une sortie .rle et l'équivalent .ppm de sa reconversion ultérieure, sous le nom de sanity_check.ppm)

Programme rlestat.exe

Consignes de l'activité synthèse

Les consignes de l'activité synthèse pour la session H2017 seront éventuellement disponibles ici (elles ne sont pas encore écrites!)

Résultats des questions quasi-hebdomadaires

Les moyennes des résultats obtenus aux questions quasi-hebdomadaires pour la session en cours suivent. Notez que l'écart-type par question n'est pas significatif étant donné l'échantillon (petit groupe) et la pondération des questions (sur cinq point, un point de différence représente , ce qui bousille quelque peu cette composante statistique).

 Question   Séance 
Q00
Q01
Q02
Q03
Q04
Q05
???
Q06
???
Q07
???
Q08
???
Q09
???
:

Résultats des travaux pratiques

Les moyennes des résultats des travaux pratiques pour la session en cours suivent.

TP Remise Poids

TP00 (consignes)

TP01 (consignes)

TP02 (consignes)

A.S.

T14
Cumulatif (sans tenir compte de l'A.S.) :
:

Code de démonstration

Vous trouverez ci-après le code source de divers exemples de vos notes de cours. Il est possible que le code ne soit pas exactement le même que dans les notes de cours puisque j'ai retouché ces dernières récemment (le site Web est un peu en retard côté mises à jour), mais les différences sont cosmétiques.

Sources des exemples du document STR – Volume 01

Cliquez sur cette cible pour obtenir le code de la chaîne pascal simplifiée

Cliquez sur cette cible pour obtenir le code de la chaîne pascal avec itérateurs

Cliquez sur cette cible pour obtenir le code du test 0.0

Cliquez sur cette cible pour obtenir le code du test 0.0b

Cliquez sur cette cible pour obtenir le code du test 0.1

Cliquez sur cette cible pour obtenir le code du test 1.0

Cliquez sur cette cible pour obtenir le code du test 1.1

Cliquez sur cette cible pour obtenir le code du test 1.2

Cliquez sur cette cible pour obtenir le code du test 1.3

Cliquez sur cette cible pour obtenir le code du test 2.0

Cliquez sur cette cible pour obtenir le code du test 2.1

Cliquez sur cette cible pour obtenir le code du test 2.2

Cliquez sur cette cible pour obtenir le code du test 3.0

En lien avec la 1re séance sous QNX

Pour la 1re séance sous QNX, vous trouverez le code source des illustrations à travers les liens suivants :

Pour l'aide en ligne de QNX au sens large, je vous invite à consulter le site http://www.qnx.com/developers/docs/6.3.2/neutrino/lib_ref/about.html qui est, somme toute, assez complet. Vous y trouverez un certain nombre de tutoriels pertinents, entre autres :

En lien avec l'exercice de compression de données

Cliquez sur cette cible pour obtenir le projet bmp00, réalisant une compression RLE sur des bitmaps dans un temps raisonnable. Ce projet n'est pas un STR puisqu'il cherche à réaliser rapidement et correctement une tâche mais n'offre aucune garantie de non-dépassement d'un seuil de temps – il n'y a aucun plafond mesurable dans le temps d'exécution de l'algorithme de compression dans ce programme, donc aucune contrainte de temps dont le respect serait déterministe.

L'idée d'offrir d'abord une version opérationnelle d'un algorithme donné, sans se préoccuper de contraintes TR, est que cette version est en général plus simple que les versions offrant des garanties TR strictes.

Sur le plan pédagogique, cela donne aussi une excuse à votre professeur pour mettre en place des bases conceptuelles clés du code moderne, pour que toutes et tous comprennent ses exemples, sans aller jusqu'à donner un cours général de techniques de programmation contemporaines. J'essaie de mettre en place des bases conceptuelles et techniques communes sans trop me répéter étant donné la nature bigarrée mais techniquement très solide de la clientèle de ce cours.

Cliquez sur cette cible pour obtenir le projet bmp-morcele, réalisant une compression RLE sur des bitmaps dans un temps raisonnable et de manière à ne pas dépasser un certain seuil de tolérance au temps.

Cette version compresse des données RGB brutes selon une approche RLE et peut, contrairement aux deux précédentes, être considérée TR (au sens usuel, pas au sens strict, mais principalement à cause de la plateforme) dans la mesure où le seuil indiquant d'arrêter tiendrait compte du pire cas possible (calculé a priori) pour une séquence de compression RLE donnée. Aller jusque là demanderait toutefois une meilleure connaissance du contexte et obscurcirait quelque peu le propos. Le code du projet est du code de test, montrant qu'il est possible de faire en sorte que l'algorithme de compression s'interrompe « en cours de traitement ».

Quelques suggestions d'exercices pour vous faire la main :

  • Ajoutez un traitement arbitraire (que vous pouvez simuler par une attente active basée sur un délai aléatoire) dans la boucle qui invoque la fonction compressant une séquence selon une approche RLE. Présumez que cette boucle doive itérer fois par seconde, donc que chaque itération de la boucle prenne au pire seconde, et faites en sorte que la compression n'utilise que le temps restant (s'il y en a). Le temps accordé à l'algorithme de compression sera donc un seuil variable plutôt que constant
  • Transformez l'algorithme de compression pour qu'il puisse être interrompu pendant une séquence RLE plutôt qu'entre chaque séquence RLE. Cette version sera globalement plus lente que la version proposée par le professeur mais sera plus près d'une version déterministe, donc moins à risque de déborder des contraintes TR imposées par le contexte. Vous voudrez utiliser un foncteur plutôt qu'une fonction pour réaliser cet exercice
  • L'algorithme qui transforme une séquence de bytes bruts en vecteur de valeurs RGB est lent. Transformez-le de manière à ce qu'il soit interruptible
  • Plus costaud : transformez la fonction qui compresse un bitmap de manière à ce qu'elle soit interruptible. Ceci sera plus facile à réaliser si l'algorithme transformant une séquence de bytes bruts en séquence RGB est lui-même interruptible. Considérez la fonction consommant un bitmap du flux comme était une opération indivisible (ceci vous permettra de vous concentrer sur l'essentiel)
  • Pour que les algorithmes soient plus déterministes, utilisez des tableaux bruts ou des vecteurs préalablement dimensionnés comme conteneurs de destination pour les algorithmes de transformation de séquence de bytes en séquence RGB et de compression RLE. Analysez la solution que vous proposez pour montrer en quoi elle est préférable à la version antérieure et en quoi elle est moins intéressante (évitez les banalités, il y a une réelle réflexion à faire ici)

Cliquez sur cette cible pour la description du format bitmap.

Cliquez sur cette cible pour la description de la compression RLE. Pour un peu de pédagogie sur RLE, voir http://hbfs.wordpress.com/2009/04/14/ad-hoc-compression-methods-rle/


Valid XHTML 1.0 Transitional

CSS Valide !