Developpez.com - C++
X

Choisissez d'abord la catégorieensuite la rubrique :


Présentation de la POA avec AspectC++

Date de publication : 22/06/2009

Par Come David (Site perso de David Come)
 

Cet article a pour but de vous introduire la programmation par aspects et son implémentation en C++: Aspect C++

               Version PDF (Miroir)   Version hors-ligne (Miroir)

I. Présentation de la POA
A. Les limites de la POO
B. Présentation de la POA et mécanismes
a. Mécanisme
b. Vocabulaire
II. Aspect C++
a. Sous Windows
b. Sous Debian et Ubuntu
III. Etude détaillée de Aspect C++
A. 1er Exemple
B. Etude détaillée


I. Présentation de la POA


A. Les limites de la POO

la POO a pour bur but de découper le programme en une série d'objets indépendants qui s'échangent des messages via leur interface publique. Cette indépendance peut être acquise entre les différents objets métiers mais le code sera toujours envahie par une série d'instructions transverse à son but premier comme le logging, la gestion des invariants ou encore le vérouillage dans un contexte multi-thread. Regardons un exemple pour mieux comprendre.

class Compte
{
//...
int m_solde;
public: 
        void depot(int valeur)
        {
        m_solde+=valeur;
        }

        void retrait(int valeur)
        {
        if(valeur<m_solde)
                m_solde-=valeur;
        else
                std::cerr<<"Impossible de retirer plus que disponible"<<std::endl;
        }
};
Cette classe Compte, qui gère un compte en banque, est tout ce qu'il y a de plus basique. Imaginons qu'a chaque transaction on souhaite enregistrer l'ancien solde, la transaction, et le nouveau solde. On peut faire quelque chose dans le style suivant:

class Compte
{
// ...
public:
        void depot(int valeur)
        {
              Logger<<"Votre solde"<<m_solde<<"avec un depot de "<<valeur<< "est de ";
              m_solde+=valeur;
              Logger<<m_solde;
        }

        void retrait(int valeur)
        {
              if(valeur<m_solde)
              {
                      Logger<<"Votre solde"<<m_solde<<"avec un retrait de "<<valeur<< "est de ";
                      m_solde-=valeur;
                      Logger<<m_solde;
              }
              else
              {
                      std::cerr<<"Impossible de retirer plus que disponible"<<std::endl;
              }
        }

};
Pour le code suivant compile, plusieurs solutions.
La première est que Logger est une variable globale. Certains diront que c'est une faute grave mais c'est envisageable, regardez std::cout. Le problème avec cela, c'est qu'on polue l'espace global et que des risques de collisions sont présents, même avec des namespaces. Enfin, on peut souhaiter appliquer différents traitements de logging en fonction des classes, ce qui est difficilement permis avec les variables globales.

La solution est donc que ce soit la classe qui soit le propriéataire de l'objet Logger. Mais dans ce cas, on perd l'indépendance du code métier, ce dernier est lié à un pan vertical du programme alors que dans un monde idéal, il ne devrait pas.

La POA souhaite arreter cet envahissement en placant le code transverse non plus dans le code métier mais dans des aspects.


B. Présentation de la POA et mécanismes


a. Mécanisme

Etant donné que la POA souhaite séparer les aspects du code, il parait normal qu'ils soient situés dans fichiers différents que ceux du code source. Mais lors du processus gloable de création du logiciel, ils doivent être intégrés au code source: c'est le rôle du tisseur. Compararons la compilation "classique" et celle avec Aspect C++

Compilation classique avec g++
Compilation classique avec g++
Compilation avec as++
Compilation avec as++
Pour les personnes familières avec Qt, on peut dire qu'Aspect C++ est similaire au Moc dans le sens où il oppère une opération pré-compilation dans un langage qui est une extension du C++.


b. Vocabulaire

La POA introduit un certain nombre de nouveaux mots de vocabulaire. Regardons les en détails car il est d'une grande importance par la suite


II. Aspect C++


a. Sous Windows


b. Sous Debian et Ubuntu

L'installation sous debian et ubuntu est la plus classique qu'il soit puisqu'il existe un paquet nommé aspectc++; Il suffit donc de faire
aptitude install aspectc++

III. Etude détaillée de Aspect C++


A. 1er Exemple

Reprenons le code du paragraphe d'introduction et rendons le compilable:
 
#include <iostream>

template <class U> class Logger{
    U& Ref;
public:

    Logger& operator<<(U& (*pf)(U&))
    {
	pf(Ref);
	return *this;
    } 

    template <class T> Logger& operator<<(const T& t)
    {
	Ref<<t;
	return *this;
    }

    Logger(U& r=std::cout):Ref(r)
    {}
}; 

class Compte
{
    Logger<std::ofstream> m_log;
    int m_solde;
// ...
public:
    Compte(int solde=100):m_log(std::cout),m_solde(solde)
    {}

    void depot(int valeur)
    {
       m_log<<"Votre ancien solde est de "<<m_solde<<std::endl;
       m_solde+=valeur;
       m_log<<"Votre nouveau solde est de "<<m_solde<<std::endl;
       std::cout<<std::endl;
    }

    void retrait(int valeur)
    {
       if(valeur<m_solde)
       {
           m_log<<"Votre ancien solde est de "<<m_solde<<std::endl;
           m_solde-=valeur;
           m_log<<"Votre nouveau solde est de "<<m_solde<<std::endl;
           std::cout<<std::endl;
       }
       else
       {
           std::cerr<<"Impossible de retirer plus que disponible"<<std::endl;
       }
    }

};
Les aspects liés au logging sont redondants. Voici le code équivalent avec les aspects
 
#include <iostream>
#include "compte.h"
    Compte::Compte(int solde):/*m_log(std::cout)*/m_solde(solde)
    {}

    void Compte::depot(int valeur)
    {
	m_solde+=valeur;
    }

    void Compte::retrait(int valeur)
    {
	if(valeur<m_solde)
	{
	    m_solde-=valeur;
	}
	else
	{
	    std::cerr<<"Impossible de retirer plus que disponible"<<std::endl;
	}
    }

int main(int argc, char const *argv[])
{
    Compte c(200);
    c.depot(10);
    return 0;
}

 
Le header qui va avec ce code:
 
#ifndef COMPTE_H 
#define COMPTE_H 

#include <sstream>

template <class U>class Logger{
    U& Ref;
public:
    //pour les manipulateurs de flux, comme std::endl
    Logger& operator<<(U& (*pf)(U&))
    {
        pf(Ref);
	return *this;
    } 

    template <class T> Logger& operator<<(const T& t)
    {
	Ref<<t;
	return *this;
    }

    Logger(U& r=std::cout):Ref(r)
    {}
}; 

class Compte
{
    int m_solde;
public:
    Compte(int solde=100);
    void depot(int valeur);
    void retrait(int valeur);
};


#endif
 
Et le fichier aspect correspondant
 

#include <iostream>
#include "compte.h"

aspect TraceCompte
{
private:
    
    pointcut what() = "Compte";
    advice what() : slice class 
		  {
		    protected: 
		      Logger<std::ostream> m_log;
                  };

    advice execution("% ...::%(...)") && within(what()) && that(compte) : around(Compte& compte)
  {
      compte.m_log <<"Avant votre solde etait de "<<compte.m_solde<<std::endl;
      tjp->proceed();
      compte.m_log<<"Avant nouveau solde est de "<< compte.m_solde<<std::endl;
  }
};
 
Je ne commenterai ici que le code lié à la programmation par aspect.


B. Etude détaillée



               Version PDF (Miroir)   Version hors-ligne (Miroir)

Valid XHTML 1.1!Valid CSS!

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Côme David. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Contacter le responsable de la rubrique C++