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

23 janvier

L00

Cours suspendus dû au verglas; vous ne perdez rien pour attendre, car une séance L15 sera ajoutée en fin de session!

25 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...

30 janvier

L01

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.

Cet exercice étant formatif, un solutionnaire vous sera proposé sous peu.

Cet exercice étant formatif, un solutionnaire a été mis à votre disposition sur Solutions-L00.html.

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.

1er février

T01

Au menu :

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

6 février

L02

Au menu :

  • Q00
  • Travail sur le TP00
    • si vous avez des questions sur le design de votre programme (en apparence simple, mais plein de subtilités amusantes), n'hésitez pas à les poser!

8 février

T02

Au menu :

  • Points techniques divers, pour vous aider à résoudre le TP00 :
    • design d'un fil d'exécution (rôle, enjeux, responsabilités, durée de vie, etc.)
    • comprendre la synchronisation dans une situation de double tamponnage avec source de données ininterruptible (mutex, condition_variable, variables atomiques, etc.)
  • Travail sur le TP00

13 février

L03

Au menu :

N'oubliez pas de remettre le TP00 à la fin de cette séance

15 février

T03

Au menu :

20 février

L04

Au menu :

Premier contact avec les pointeurs intelligents (voir smart_ptr.pdf pour la présentation électronique sur le sujet)

22 février

T04

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 n'avons pas terminé de couvrir le sujet...

27 février

L05

Au menu :

  • Poursuite de notre examen de la gestion de la mémoire
  • Taille et alignement des objets : les opérateurs sizeof, alignof et alignas
  • Allocation positionnelle (Placement New)
  • Allocation assistée
  • Allocation par type
  • Gérer la mémoire dans une Arena

Nous avons entre autres 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
}

1er mars

T05

Au menu :

6 mars

Journée de mise à niveau (cours suspendus)

8 mars

Journée de mise à niveau (cours suspendus). Bonne Journée internationale des femmes!

13 mars

L06

Je serai hors du pays pour la rencontre du WG21 à Jacksonville. Si vous le souhaitez, vous pouvez suivre mes aventures sur ../../../Sujets/Orthogonal/wg21-2018-Jacksonville.html

Pour ne pas perdre la main d'ici la semaine prochaine :

  • 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?

15 mars

T06

Je serai hors du pays pour la rencontre du WG21 à Jacksonville. Si vous le souhaitez, vous pouvez suivre mes aventures sur ../../../Sujets/Orthogonal/wg21-2018-Jacksonville.html

20 mars

L07

  • Rapport de voyage, en particulier les enjeux qui touchent directement notre cours :
    • Annotation [[no_unique_address]], utile en particulier dans le cas des classes terminales
    • Annotations [[likely]] et [[unlikely]]
    • Contrats
    • Destroying operator delete
    • <span>
    • Itérateurs constexpr
    • simd<T> pour le Parallelism TS v2
    • Static Reflection TS
    • Coroutines et transfert de contrôle symétrique
  • Aussi intéressants, mais d'un autre ordre :
    • SG15
    • SG16
    • Dates et fuseaux horaires
    • Down with typename!
    • Destructeurs constexpr
    • Travaux sur la syntaxe concise des concepts
    • λ et capture variadique
    • Affectation déstructurante (Structured Bindings) et accessibilité
    • J'ai appris que les qualifications d'accès voyagent dans le temps :
struct A {
protected:
  struct B {
  };
};
struct X : A::B, A {
};
int main() {
  X x;
}
    • Cas analogues au problème de l'arrêt
#include <cstdio>
#include <cstdlib>
void f() {
  struct X {
    ~X() {
      std::puts("unwound");
      std::exit(0);
    }
  } x;
  throw 0;
}
int main(int argc, char**) {
  try {
    f();
  } catch (int) {
    std::puts("caught");
  }
}
    • Quelques horreurs (toujours amusantes), par exemple :
struct A {
   int n = A{}.n;
}; 
    • ... ou encore :
struct A { int x, y; };
A passthrough(A a) { return a; }
int main(void) {
   A a;
   a.x = 0;
   return passthrough(a).x; // Oups! UB!
}

Retour sur le problème de la file_circulaire

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

22 mars

T07

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

27 mars

L08

Au menu :

Un programme de test possible serait :

#include <new>
#include <functional>
#include <vector>
#include <random>
#include <chrono>
#include <numeric>
#include <algorithm>
#include <iostream>
#include <utility>
#include <memory>
#include <cassert>
#include <numeric>
#include <bitset>
using namespace std;
using namespace std::chrono;

namespace kh2 {
   namespace v0 {
      //
      // version archi naïve, qui ne respecte pas les consignes,
      // écrite juste pour que le code de test compile
      //
      class GestionnaireBlocs {
      public:
         GestionnaireBlocs() = default;
         GestionnaireBlocs(const GestionnaireBlocs&) = delete;
         GestionnaireBlocs& operator=(const GestionnaireBlocs&) = delete;
         void * allouer(size_t n) {
            return ::operator new(n); // délègue au new standard
         }
         void liberer(void *p) {
            ::operator delete(p); // délègue au delete standard
         }
         //
         // pour afficher les statistiques
         //
         friend ostream& operator<<(ostream &os, const GestionnaireBlocs &) {
            return os << "Version 0";
         }
      };
   }
}

using kh2::v0::GestionnaireBlocs;

void * operator new(size_t n, GestionnaireBlocs &gb) {
   return gb.allouer(n);
}
void operator delete(void *p, GestionnaireBlocs &gb) {
   return gb.liberer(p);
}

template <int N>
struct moton {
   char _[N] {};
};

//template <int N>
//void assassin(void *p) {
//   static_cast<moton<N>*>(p)->~moton<N>();
//}
//
template <int N>
pair<void*, function<void(void*)>> creer_moton(GestionnaireBlocs &gb) {
   return pair<void*, function<void(void*)>>{
      new (gb) moton<N>,
      [&gb](void *p) {
         static_cast<moton<N>*>(p)->~moton<N>();
         gb.liberer(p);
      }
   };
}

int main() {
   GestionnaireBlocs gb;
   int tres_petits = 0;
   int petits = 0;
   int pas_gros = 0;
   int autres = 0;
   vector<function<pair<void*, function<void(void*)>>(GestionnaireBlocs&)>> ges = {
      [&](GestionnaireBlocs &gb) {
         ++tres_petits;
         return creer_moton<1>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++tres_petits;
         return creer_moton<2>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++tres_petits;
         return creer_moton<4>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++tres_petits;
         return creer_moton<7>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++tres_petits;
         return creer_moton<8>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++tres_petits;
         return creer_moton<31>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++tres_petits;
         return creer_moton<32>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++petits;
         return creer_moton<33>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++petits;
         return creer_moton<50>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++petits;
         return creer_moton<63>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++petits;
         return creer_moton<64>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++pas_gros;
         return creer_moton<65>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++pas_gros;
         return creer_moton<127>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++pas_gros;
         return creer_moton<128>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++autres;
         return creer_moton<129>(gb);
      },
      [&](GestionnaireBlocs &gb) {
         ++autres;
         return creer_moton<200>(gb);
      }
   };
   enum { N = 1'000'000, NTESTS = 100 };
   vector<void *> ptrs;
   vector<function<void(void*)>> deleters;
   ptrs.reserve(N);
   deleters.reserve(N);
   // random_device rd;
   mt19937 prng{ 0 /* rd() */ };
   uniform_int_distribution<decltype(ges.size())> de{ 0, ges.size() - 1 };
   vector<high_resolution_clock::duration> temps_par_test;
   for (auto t = 0; t != NTESTS; ++t) {
      auto avant = high_resolution_clock::now();
      for (int i = 0; i != N; ++i) {
         auto pos = de(prng);
         auto [ptr, del] = ges[pos](gb);
         ptrs.push_back(ptr);
         deleters.push_back(del);
      }
      for (vector<void*>::size_type i = 0; i != ptrs.size(); ++i) {
         deleters[i](ptrs[i]);
      }
      auto apres = high_resolution_clock::now();
      ptrs.clear();
      deleters.clear();
      temps_par_test.emplace_back(apres - avant);
      cout << '.' << flush;
   }
   auto temps_total = accumulate(
      begin(temps_par_test), end(temps_par_test), high_resolution_clock::duration{}
   );
   if (tres_petits + petits + pas_gros + autres != N * NTESTS)
      cerr << "Erreur suspecte de calcul (ceci ne devrait pas s'afficher)" << endl;
   cout << "\n\nNombre de tests : " << NTESTS
        << "\nNombre d'allocations par test : " << N
        << "\nNombre de [0,32] bytes) : " << tres_petits
        << "\nNombre de (32,64] bytes : " << petits
        << "\nNombre de (64,128] bytes : " << pas_gros
        << "\nNombre de (128+ bytes : " << autres
        << "\nTemps ecoule (total) : " << duration_cast<milliseconds>(temps_total).count() << " ms."
        << "\nTemps ecoule (moyen par test) : "
        << static_cast<double>(duration_cast<milliseconds>(temps_total).count()) / NTESTS << " ms." << endl;
   cout << gb << endl;
}

 

29 mars

Journée pédagogique (cours suspendus)

3 avril

L09

Au menu :

5 avril

T08

Au menu :

  • Travail sur le TP01

10 avril

L10

Au menu :

  • Travail sur le TP01

Le TP01 est à remettre au plus tard à la fin de la séance d'aujourd'hui.

12 avril

T09

La séance n'a pas eue lieu, votre chic prof ayant un rendez-vous médical avec l'un de ses enfants.

17 avril

L11

Au menu :

19 avril

T10

Au menu :

  • Présentation du TP02
  • Travail sur le TP02

Attention : vous n'aurez pas énormément de temps en classe pour faire ce travail, car les prochaines séances seront quelque peu chargées, alors réservez-vous du temps hors-classe pour être en mesure de livrer à temps. TP02 n'est pas un très gros travail, mais il faut quand même lui accorder un peu de temps pour y arriver de manière satisfaisante

24 avril

L12

Au menu :

26 avril

T11

Au menu :

1er mai

L13

Cours suspendu, votre chic prof devant aller à l'hôpital avec son plus jeune

3 mai

T12

Au menu :

Le TP02 est à remettre au plus tard au début de la séance d'aujourd'hui.

8 mai

L14

Au menu :

10 mai

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

11 mai

T13

Au menu :

Attention : vendredi avec horaire du jeudi

15 mai

L15

Au menu :

  • Travail sur l'A.S.

17 mai

T14

Au menu :

  • Q08, dont l'énoncé va comme suit :
  • Pour deux points, écrivez la fonction générique taille_plusse_grand(T,U) qui retourne à la compilation la taille du type d'entre T et U qui occupe le plus grand espace en mémoire (utilisez sizeof pour obtenir cette taille)
  • Pour deux points, écrivez la classe Moton<T,U> qui aura comme attribut un tableau nommé buf de type unsigned char dont la capacité sera la taille du type d'entre T et U qui occupe le plus grand espace en mémoire
  • Pour un point, ajoutez à Moton<T,U> un constructeur prenant un T et créant un T dans buf à l'aide d'un Placement New
  • Pour un point, ajoutez à Moton<T,U> un constructeur prenant un U et créant un U dans buf à l'aide d'un Placement New
  • Envoyez-moi le code de votre réponse dans une pièce jointe à un message Colnet d'ici dimanche le 20 mai 2018 à 23 h 59

Maintenant que la date limite pour soumettre votre réponse à la question est passée, sachez qu'un solutionnaire possible serait :

//
// Pour les deux premiers points
//
template <class T, class U> // fonction générique sur deux types T et U
   constexpr auto // auto peut être remplacé par size_t
      taille_plusse_grand(T, U) { // pas besoin de nommer les paramètres s'ils ne sont pas utilisés
      return sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U); // ou std::max(sizeof(T), sizeof(U))
   }
//
// Pour les deux points suivants
//
template <class T, class U>
   class Moton { // ou struct Moton, c'est comme vous voulez
      // notez que la question ne discutait pas d'alignement, mais il aurait été sage de s'en occuper ici
      unsigned char buf[taille_plusse_gros(T{}, U{})}; // avec des types T et U appropriés :)
   public:
      //
      // Pour les deux points restant
      //
      Moton(const T &obj) : new (buf) T{ obj } {
      }
      Moton(const U &obj) : new (buf) U{ obj } {
      }
   }; 
  • Q09
    • À remettre (la grille seulement, pas les définitions) sous ma porte de bureau au plus tard mardi le 22 mai àh
  • Travail sur l'A.S.
  • Remise de l'A.S.

Consignes des travaux pratiques

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

Consignes Document d'accompagnement

TP00

TP00--Consommateur.zip

TP01

Voir le code de test proposé à la séance XXX

TP02

Outil pour convertir de PPM à RLE (à titre comparatif) : ppm_to_rle.exe

Outil pour tirer des statistiques d'un fichier RLE (pour aider au débogage) : rlestat.exe

Notez que ppm_to_rle.exe ne fonctionnera pas avec un fichier PPM contenant des commentaires (j'ai été trop lâche...)

Consignes de l'activité synthèse

Les consignes de l'activité synthèse pour la session H2018 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 !