Un vecteur hétérogène

Description

Voici un démonstrateur qui montre comment on peut implémenter en C++ un vecteur hétérogène d'éléments ayant divers types différents natifs ou construits par l'utilisateur. Pour avoir une programmation efficace et claire il convient d'utiliser STL et boost::any.

La bibliothèque STL incluse dans le C++ permet l'emploi du conteneur Vector. La bibliothèque Boost aura dans les versions futures de C++ de plus en plus de composantes. En attendant, pour les utilisateurs qui n'ont pas Boost c'est très simple. Dans un premier temps il suffit de télécharger à https://www.boost.org/ le fichier zip de 158 Mo de la version actuelle. Ensuite on extrait et on range le répertoire boost de 120 Mo contenant les références et on renseigne le compilateur C++ avec l'endroit de ce rangement à l'adresse nécessaire pour trouver les fichiers #include. On peut obtenir très facilement sur Internet diverses documentations et exemples relatifs à Boost mais pour boost::any ce n'est pas indispensable.

Le démonstrateur présent utilise en tant que types connus ici : int, char, std::string et MaClasse. MaClasse est une classe exemple qui sert à montrer qu'on peut gérer aussi des éléments définis par l'utilisateur. Ce démonstrateur montre comment effectuer :
- le rangement d'un élément de type quelconque en fin du vecteur,
- le décompte des éléments suivant chaque type connu,
- le parcours et l'affichage de tout le vecteur hétérogène,
- l'affichage direct d'un seul élément désigné du vecteur,
- la mise à jour du type et de la valeur d'un élément du vecteur,
- l'obtention du type connu et de la valeur d'un élément du vecteur,
- la sauvegarde dans un fichier de tout le vecteur,
- la restitution d'une copie du vecteur à partir d'un fichier.
D'autres fonctionnalités ne sont pas programmées ici parce qu'il est très facile de les obtenir avec des méthodes analogues, en particulier : ajouter ou supprimer un élément au début, au milieu ou à la fin du vecteur.

Il est commode de regarder à côté du source l'image main.jpg que l'on obtient en effectuant l'exécution de ce démonstrateur.
Ce programme est effectué avec Visual Studio Express 2012 pour Windows Desktop.
Ce programme n'est pas une application opérationnelle, l'utilisateur intéressé devra adapter à ses besoins les morceaux de programme qui le concernent.

Source :

 
/*******************************************
*   Vecteur hétérogène  -  @author pgl10   *
*  un démontrateur avec STL et boost::any  *
*******************************************/

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <boost/any.hpp>

// Définition d'une classe, pour l'exemple
class MaClasse
{
    std::string _s;
    public :
    MaClasse(const std::string& s) : _s(s) { }
    std::string getString() {return _s;}
};

// Fonctions permettant d'établir une correspondance entre le type actuel
// d'un objet boost::any et les types connus : int, char, std::string et MaClasse
bool is_int(const boost::any& a)
{
    return a.type() == typeid(int);
}
bool is_char(const boost::any& a)
{
    return a.type() == typeid(char);
}
bool is_std_string(const boost::any& a)
{
    return a.type() == typeid(std::string);
}
bool is_ma_classe(const boost::any& a)
{
    return a.type() == typeid(MaClasse);
}

// Pour identifier le type connu d'un boost::any
// Chaque type connu a un numéro de type spécifique
int batype(boost::any a)
{
    int n = 0;
    if(is_int(a)) n = 1;
    if(is_char(a)) n = 2;
    if(is_std_string(a)) n = 3;
    if(is_ma_classe(a)) n = 4;
    return n;
}

// Pour convertir en std::string un boost::any de type int
std::string bai2str(boost::any a)
{
    if(!is_int(a)) return "";
    std::stringstream ss;
    ss << boost::any_cast<int>(a);
    return std::string(ss.str());
}

// Pour pouvoir afficher le contenu du n-ième élément de values
std::string element(std::vector<boost::any> values, unsigned int n)
{
    if(n<1 || n>values.size()) return "element inexistant";
    // le n-ième élément a le rang n-1
    n = n-1;
    std::string str;
    try
    {
        if(is_int(values[n])) str = bai2str(values[n]);
        else if(is_char(values[n])) str = boost::any_cast<char>(values[n]);
        else if(is_std_string(values[n])) str = boost::any_cast<std::string>(values[n]);
        else if(is_ma_classe(values[n])) str = boost::any_cast<MaClasse>(values[n]).getString();
        else str = "type non reconnu";
    }
    catch(boost::bad_any_cast& bac) 
    {
        str = bac.what(); 
    }
    return str;
}

// Pour sauvegarder dans un fichier un vecteur d'éléments boost::any
void outvba(std::string fichier, std::vector<boost::any> vecteur)
{
    std::ofstream monfic;
    monfic.open(fichier.c_str());
    auto it = vecteur.begin();
    for(unsigned int  i=0; i < vecteur.size(); ++i, ++it) 
    {
        try
        {
            if(is_int(*it)) monfic << batype(*it) << " " << boost::any_cast<int>(*it) << std::endl;
            else if(is_char(*it)) monfic << batype(*it) << " " << boost::any_cast<char>(*it) << std::endl;
            else if(is_std_string(*it)) monfic << batype(*it) << " " << boost::any_cast<std::string>(*it) << std::endl;
            else if(is_ma_classe(*it)) monfic << batype(*it) << " " << boost::any_cast<MaClasse>(*it).getString() << std::endl;
        }
        catch(boost::bad_any_cast& bac) 
        {
            monfic << batype(*it) << " " << bac.what() << std::endl;
        }
    }
    monfic.close();
}

// Pour restituer un vecteur d'éléments boost::any à partir d'un fichier
void invba(std::string fichier, std::vector<boost::any>& vecteur)
{
    std::ifstream monfic;
    std::string ligne;
    monfic.open(fichier.c_str());
    while(getline(monfic, ligne)) 
    {
        std::stringstream ss;
        ss << ligne;
        int ntype;
        ss >> ntype;
        std::size_t found = ligne.find(" ");
        ligne = ligne.substr(found+1);
        if(ntype == 1) vecteur.push_back(int(atoi(ligne.c_str())));
        if(ntype == 2) vecteur.push_back(ligne[0]);
        if(ntype == 3) vecteur.push_back(ligne);
        if(ntype == 4) vecteur.push_back(MaClasse(ligne));
    }
    monfic.close();
}

int main()
{
    // Création d'un vecteur contenant des boost::any
    std::vector<boost::any> values;
    // Rangement dans values de plusieurs éléments de types différents
    values.push_back(int(1));
    values.push_back(int(12));
    values.push_back(int(123));
    values.push_back(char('s'));
    values.push_back(char('v'));
    values.push_back(char('p'));
    values.push_back(std::string("blablabla"));
    values.push_back(std::string("ceci est un texte"));
    values.push_back(MaClasse("boost::any"));
    values.push_back(MaClasse("pour stocker divers types"));
    values.push_back(double(3.1416));
    char* texte = "Bonjour !";
    values.push_back(texte);
    // On compte le nombre d'entiers, de caractères, de std::string 
    // et de MaClasse présents dans le vecteur values
    std::cout << "Nombre d'202lements = " << values.size() << std::endl;
    std::cout << "Nombre d'entiers = " << count_if(values.begin(), values.end(), is_int) << std::endl;
    std::cout << "Nombre de caract212res = " << count_if(values.begin(), values.end(), is_char) << std::endl;
    std::cout << "Nombre de std::string = " << count_if(values.begin(), values.end(), is_std_string) << std::endl;
    std::cout << "Nombre de MaClasse = " << count_if(values.begin(), values.end(), is_ma_classe) << std::endl;
    // On affiche le contenu du vecteur hétérogène values
    auto it = values.begin();
    for(unsigned int  i=0; i < values.size(); ++i, ++it) 
    {
        try
        {
            std::cout << "Element num202ro " << i << " : ";
            if(is_int(*it)) std::cout << "int = " << boost::any_cast<int>(*it);
            else if(is_char(*it)) std::cout << "char = " << boost::any_cast<char>(*it);
            else if(is_std_string(*it)) std::cout << "std::string = " << boost::any_cast<std::string>(*it);
            else if(is_ma_classe(*it)) std::cout << "MaClasse = " << boost::any_cast<MaClasse>(*it).getString();
            else std::cout << "type non reconnu";
            std::cout << std::endl;
        }
        catch(boost::bad_any_cast& bac) 
        {
            std::cout << bac.what() << std::endl; 
        }
    }
    // On affiche directement et seulement le 8-ième élément de values
    std::cout << "Le 8-ieme 202l202ment = " << element(values, 8) << std::endl;
    // On modifie puis on restitue le type et la valeur du 1-er élément de values
    std::cout << "values[0] = " << element(values, 1) << std::endl;
    values[0] = 'a';
    std::cout << "values[0] = " << element(values, 1) << std::endl;
    values[0] = 1;
    // Pour afficher le type connu actuel de deux boost::any
    std::cout << "Le type actuel de values[0] = " << batype(values[0]) << std::endl;
    std::cout << "Le type actuel de values[9] = " << batype(values[9]) << std::endl;
    // Pour extraire de values[9] une copie de son contenu (sa valeur)
    MaClasse mc = boost::any_cast<MaClasse>(values[9]);
    std::cout << "mc.getString() = " << mc.getString() << std::endl;
    // Pour sauvegarder le vecteur values
    std::string fichier = "monfichier.txt";
    outvba(fichier, values);
    // Pour restituer une copie de values avec seulement les éléments de types connus
    std::vector<boost::any> newvals;
    invba(fichier, newvals);
    std::cout << "Nombre d'202lements de newvals = " << newvals.size() << std::endl;
    return 0;
}

Conclusion :

Merci pour vos commentaires et remarques.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.