Tableau contenant des valeurs de types différents

mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012 - 1 sept. 2011 à 15:37
mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012 - 1 sept. 2011 à 19:36
Bonjour,

Voilà, "nouveau" en développement c++,
je me demande comment puis-je faire, de la manière la plus propre possible, un tableau contenant des pointeurs de classes de différentes natures.

Pour être plus explicite, par exemple :
case 0=>String*
case 1=>Integer*
case 2=>Double*
case 3=>String*

Mais bien entendu, l'ensemble des types disponibles est connu, mais les types de chaque case sont décidés par l'utilisateur (par exemple lors de la lecture d'un fichier xml, mais c'est un détail a mon avis inutile ici).

Pour préciser, je sais parfaitement comment j'aurais fait en C, et je saurais le faire en C++, d'une manière plus ou moins identique, mais j'aimerais surtout savoir comment ce genre de problématique peut se résoudre, "d'une manière propre" en C++ ?

Merci d'avance :)


  Qui ne tente rien...
  Ne risque pas d'avoir grand chose !!!

13 réponses

cs_Julien39 Messages postés 6414 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 29 juillet 2020 367
1 sept. 2011 à 16:55
Bonjour,

La première question à se poser dans ce genre de cas est : est il bien nécessaire de n'avoir qu'un seul tableau ?

Si oui, j'ai fais un tutoriel en java qui permet de faire proprement ce genre de choses, le principe en C++ est identique : http://www.javafr.com/codes/TUTORIEL-CREER-LISTE-CONTIENT-DEUX-TYPES-OBJETS_53352.aspx

Bon courage
0
mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012
1 sept. 2011 à 17:34
Merci pour cette réponse rapide.

Par contre, j'ai beau tenter de faire



class Vehicule
{
public:
virtual void payer();
};

class Voiture:Vehicule
{
void payer();
}


listeVehicules.add(new Voiture()) ;

le compilateur me dit :
main.cpp:9: error: ?Vehicule? is an inaccessible base of ?Voiture?


Qui ne tente rien...
Ne risque pas d'avoir grand chose !!!
0
pop70 Messages postés 181 Date d'inscription mardi 6 avril 2010 Statut Membre Dernière intervention 7 janvier 2012 10
1 sept. 2011 à 17:37
Salut,
tout d'abord faire ce que tu souhaites faire, à savoir stocker des types différents dans un même tableau est très déconseillé voici un code qui te fera comprendre pourquoi (mais aussi comment faire )

#include 


typedef unsigned int* universel;   /* Crée un type nommé "universel" équivalent à un pointeur sur un entier non-signé (tu peux mettre n'importe quel autre type de pointeur */

int main()
{

// Trois variables de types différents :
    std::string texte = "Un texte simple";
    int entier = 7;
    double decimal = 6.3;

// Leur pointeurs respectifs :
    std::string *ptr_texte = &texte;
    int *ptr_entier =  &entier;
    double *ptr_decimal = &decimal;


    universel tableau[3]; // Le fameux tableau de type universel

// Pour remplir ce tableau il faut transtyper toutes les valeurs qui y rentre :
    tableau[1] = (universel)ptr_texte; 
    tableau[2] = (universel)ptr_entier;
    tableau[3] = (universel)ptr_decimal;



    std::cout << "Universel[1] = " << *tableau[1] << "\n";                  // Affiche un nombre quelconque
    std::cout << "Universel[1] = " << *(string*)tableau[1] << "\n\n";       // Affiche "Un texte simple
    std::cout << "Universel[2] = " << *tableau[2] << "\n";                  // Affiche 7
    std::cout << "Universel[2] = " << *(int*)tableau[2] << "\n\n";          // Affiche 7
    std::cout << "Universel[3] = " << *tableau[3] << "\n";                  // Affiche un nombre quelconque
    std::cout << "Universel[3] = " << *(double*)tableau[3] << "\n";         // Affiche 6.3

    return 0;
}


Dans ce code on voit que si à l'affichage on a oublié le type de la valeur qui est contenue et qu'on ne la transtype pas, on peut avoir n'importe quoi à la sortie.
Et encore, ce que tu cherches à faire est un tableau de pointeurs, donc d'éléments qui ont toujours la même taille. Mais si jamais tu fais pareil pour stocker des valeurs, il y aura une perte d'informations : si par exemple tu stockes tout dans un tableau de char, les éléments devrons faire 1 octet, or si tu y met un entier, il vas changer de valeur car celui-ci se code sur 4octets et trois seront perdus... Bref ça fonctionne avec des pointeurs, mais pas avec des types. Et lorsque c'est avec des pointeurs, tu dois savoirs "quelle case contient quel type" pour pouvoir récupérer correctement la valeur contenue...

C++dialement

Pop70
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
1 sept. 2011 à 17:56
1) Il y a un problème de conception. On ne mélange jamais des types de données, quelque soit le langage d'ailleurs.
2) Dans le cas d'un héritage, c'est différent car les types ont une base commune. On peut donc faire rentrer une classe "fille" dans une classe "mere" sans souci. Ce que tu as fait est bien. Tu as juste oublié de faire un héritage public (par défaut c'est privé). Donc la bonne manière est: class Voiture : public Vehicule
3) Enfin, quand bien même tu voudrais toujours mélanger des types, tu as le boost::variant, et le boost::any qui peuvent répondre à cette problématique.

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012
1 sept. 2011 à 17:56
Merci, ca me décrit la méthode pour le faire en C, et effectivement ca marche, en dehors du fait que je ferait un truc du style

struct entrie
{
void *valeur;
char *(to_string)(void *valeur);
}

ou autre qui permet de conserver des infos sur la valeur enregistrée.
Là, je demande surtout la méthode la plus classe, pour :
imaginons que je veuille avoir une classe dont le contenu est défini par l'utilisateur (un peu genre une table SQL),
mais je voudrais éviter de tout stocker sous format texte.

Donc, quelle est la meilleure manière de le faire en C++ ?

J'étais parti sur un tableau, ou une liste de pointeurs associés a une clé.

  Qui ne tente rien...
  Ne risque pas d'avoir grand chose !!!
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
1 sept. 2011 à 18:00
Peux-tu préciser ton problème ? Que cherches-tu à faire au final ?
Je peux sûrement t'orienter sur la bonne méthode, mais il me faut connaître la finalité. Il n'y a malheureusement pas de conception universelle.

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012
1 sept. 2011 à 18:13
OK.

Mon but est de créer une sur-couche a une base de données.
Cette sur-couche permettra une gestion supplementaire des données que la base ne permet pas de faire.
Pour se faire il me faut une manière de représenter les tables de la base, dans une structure ou dans une classe. mais je la veux générique tant qu'à faire.

Je ne sais pas si c'est assez clair, malheureusement je ne peux actuellement préciser beaucoup plus.

A vrai dire, les questions d'héritage je maitrise encore très mal.
Je ne savais pas qu'on pouvais le faire public. De meme, accéder à une méthode de la fille en ayant un pointeur sur la mère (Grace au virtuel ?)

  Qui ne tente rien...
  Ne risque pas d'avoir grand chose !!!
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
1 sept. 2011 à 18:27
Si tu cherches à réaliser une surcouche, vérfie déjà que le produit n'existe pas (genre mysql++ ?).
Ton problème n'est pas très clair. Je vais essayer de supposer.

Si tu veux le faire par toi même par simple curiosité, l'héritage n'est pas la bonne manière.
Si ton but est d'avoir une répresentation colonne - type - valeur, il y a plusieurs approches.

Avec du template, tu peux créer une classe suffisamment générique pour représenter un nombre de colonne prédéfini(En C++0X en tout cas, en C++ "normal", ce n'est pas possible).

Exemple bidon pour une classe à trois types fixes sélectionnables:
template <typename FirstType, typename SecondType, typename ThirdType>
struct Row
{
  FirstType  _a;
  SecondType _b;
  ThirdType  _c;
};

Row row(1, 1.0, 54.56);
Row<std::string, int, int> row("test", 5, 4);

// Tu peux ensuite faire un std::vector<Row<std::string, int, int> > ou std::list<Row<std::string, int, int> >
// et un std::vector<Row > ou std::list<Row >


Ou alors tu peux utiliser boost::tuple, qui est une version améliorer de mon exemple (avec un nombre de colonne quasi-illimité, fonctionne en C++ "normal").
Ce qui donnerait directement:
std::vector >
std::vector >
std::vector >
std::vector >
// etc...


PS: class et struct en C++ c'est strictement identique, seul le niveau de visibilité par défaut varie. (struct public, class private).

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012
1 sept. 2011 à 18:37
J'ai déjà vérifié si ça existait, mais je n'ai rien trouvé qui me convienne.

Tes solutions sont biens, mais hardcodées non ?
Quand je disais modulable, c'est à la volée.
La classe doit se calquer sur la structure de la base au démarrage de l'application.

  Qui ne tente rien...
  Ne risque pas d'avoir grand chose !!!
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
1 sept. 2011 à 18:55
Il y a une différence entre hardcodé et calculé à la compilation. Ici c'est effectivement calculé à la compilation.

Dans ce cas, ça change tout, il te faut représenter à la volée cette association. Ce qui va être particulièrement pénible. Chercherais-tu à recoder un ORM (Object Relational Mapping) ? Dans ce cas: http://en.wikipedia.org/wiki/List_of_object-relational_mapping_software

Pour répondre à ta question:

Tu fais un enum (SQLType) par type SQL existant dans ta base.

Tu fais ensuite une classe Table qui peut ajouter autant de SQLType que nécessaire, associé à un nom de colonne.
(utilise un std::map<std::string, SQLType>) ce qui te permet de créer une structure.
Enfin, il te faut une méthode add à ta classe Table qui prend en argument un objet de type Row qui est générique.
C'est-à-dire qu'il possède un std::map<std::string, SQLValue> (avec SQLValue qui possède un SQLType + la valeur).
Lorsque tu ajoutes à l'aide de add, il te faut vérifier que le "Row" est compatible avec la structure définie, et ajouter les valeurs à la table si c'est le cas.

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012
1 sept. 2011 à 19:17
Cool, je suis content de voir que tu viens pour ainsi dire de décrire la méthode sur laquelle j'étais parti.

Par contre, concernant la SQLValue, j'ai des doutes entre deux possibilitês :
Faire la différence entre les types via un switch dans chaque fonction qui le necessite, ou alors faire une classe virtuelle SQLValue et une classe fille du type concerné pour chacun des types de donnée ?


  Qui ne tente rien...
  Ne risque pas d'avoir grand chose !!!
0
cptpingu Messages postés 3835 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 1 novembre 2022 124
1 sept. 2011 à 19:22
Si c'est pour vérifier que deux SQLValue sont identiques, un simple == suffit, non ?

Sinon, d'une manière générale, on ne fait des classes que si celles-ci embarquent une logique. Si elles ne sont là que pour être une information à elles toutes seules, alors c'est bien des enums qu'il te faut.
Tout dépend de ce que tu recherches. A noter que l'héritage (dynamique) peut avoir un coût.

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0
mondrone Messages postés 246 Date d'inscription mercredi 5 janvier 2005 Statut Membre Dernière intervention 11 mars 2012
1 sept. 2011 à 19:36
En fait, il va me falloir au moins un "to string" et un "from string". Donc, ok, mieux vaut un switch ici. Disons que venant du C, j'ai pour le moment du mal à bien le rendre compte de la nécessité où non de faire une classe pour tel donnée.

Merci pour ces réponses

  Qui ne tente rien...
  Ne risque pas d'avoir grand chose !!!
0