OVH Cloud OVH Cloud

[Debutan C++] Retour pointeur

51 réponses
Avatar
PtitMat
Bonjour,

Je voudrais ecrire une fonction qui me retourne un pointeur vers une
variable.
Est-ce possible?
Je vous remercie pour votre aide

Mat.

10 réponses

1 2 3 4 5
Avatar
PtitMat
Christophe Lephay wrote:
PtitMat wrote:

Ce qui se passe :
J'ai un ordinateur composé d'une rom et d'un cpu
La rom est initialisé au début avec les instructions sous la forme
d'un tableau d'entier.
Une insrtuction a 3 chifres (341 ar exemple)
Le chifre numéro 1 appelé d1 (digit 1) indique le type d'opération
assembleur
Les chiffres 2 et 3 (d2 et d3) sont les opérandes.
Beaucoup de fonctions ont des operandes qui sont des numéros de
registres du CPU
Exemple move_value (value, Registre)
Ajoute(ValueRx, ValueRy)
Mes registres sont appelés R0 a R3

Ma fonction move_value (value, Registre)
appelle la fonction dToR pour convertir le digit qui lui est passé en
paramètre en pointeur vers le registre de manière a écrire la valeur
dan le bon registre.

J'ai pensé intelligent avec mon niveau de C++ de faire comme j'ai fait
mais 'est vrai qu'un swtch est plus propre.
De plus une gestion de l'erreur a se niveaux est sécuritaire, mais je
ne conaiisait pas cette commande :
default: throw( "erreur" );



Le plus idiomatique serait de créer deux hiérarchies : une pour les
instructions, une autre pour les opérandes, puis une factory ("usine"
littéralement") qui te renverrait un pointeur sur un objet dérivé
instruction qui aurait lui-même extrait les opérandes de manière approprié.
Si tu débutes en C++, celà représente pas mal de choses à apprendre d'un
coup, mais tu bénéficierais de tous les avantages de la POO (en
l'occurrence, tu pourrais par la suite créer de nouvelles instructions sans
jamais avoir à changer le code qui aurait déjà été écrit, le rendant ainsi
facilement adaptable à d'autres CPUs).

En l'occurrence, la manière idiomatique de coder celà se ferait sans aucun
switch (une factory est généralement basée sur un tableau, éventuellement
associatif, de pointeurs de fonctions chargées chacune de construire des
objets d'un type spécifique). J'hésite à en dire plus à ce stade vu que tu
sembles débuter en C++. Si tu veux plus de détails, demande le en réponse à
ce post.

Chris


Je commence par dejà apprendre a utiliser les exeptions avec les

réponses précédentes.
Chaque chose en son temps.
La factory sera pour plus tard (je vai eviter de faire une usine a gaz
parfaite pour me perdre dans les vapeurs)... ;-)

Mat.


Avatar
Christophe Lephay
PtitMat wrote:
Je commence par dejà apprendre a utiliser les exeptions avec les
réponses précédentes.
Chaque chose en son temps.
La factory sera pour plus tard (je vai eviter de faire une usine a gaz
parfaite pour me perdre dans les vapeurs)... ;-)


class instruction
{
...
virtual void extrait_operandes( std::istream& ) = 0;

public:
virtual ~instruction() {}
};


class ajoute : public instruction
{
char valeur;
reg registre;

void extrait_operandes( std::istream& is )
{
// extraire depuis le flux les opérandes requis
...
}

public:
// la fonction fabrique
static ajoute * make( std::istream& is )
{
ajoute * instr = new ajoute;
instr->extrait_operandes( is );
return instr;
}

};

classe soustrait : public instruction
{
char valeur;
reg registre;

void extrait_operandes( std::istream& is )
{
// extraire depuis le flux les opérandes requis
...
}

public:
// la fonction fabrique
static soustrait * make( std::istream& is )
{
soustrait * instr = new soustrait;
instr->extrait_operandes( is );
return instr;
}

};

Tu peux faire un fabrique sommaire basée sur un map :

typedef instruction * (* pf ) ( std::istream& ); // typedef pour pointeur
sur fonction
std::map< char, pf > fabrique;

// initialisation de la fabrique :
fabrique[ '1' ] = ajoute::&make;
fabrique[ '2' ] = soustrait::&make;
...

// disons que la rom est mappée dans le fichier "rom" :
std::ifstream ifs( "rom" );

// lecture du premier digit, code de l'instruction
char c;
ifs.get( c );

// recherche du digit
std::map< char, pf >::iterator it;
it = fabrique.find( c );

// erreur si l'instruction n'est pas reconnue par la fabrique
if( it == fabrique.end() )
throw( "instruction invalide" );

// dans le cas contraire, on appelle la fonction make associée via
déréférencement de l'itérateur
instruction * instr = (*it)( ifs ); // appel de la fonction make
...



En gros, ça te donne l'idée. Bonne digestion ;)

Chris

Avatar
Pierre Maurette
PtitMat a écrit:
[...]
Voici ma fonction écrite selon vos conseils :

int * Ccpu::dToR (int dx)
{
if (dx == 0)
{
return pR0;
}
if (dx == 1)
{
return pR1;
}
if (dx == 2)
{
return pR2;
}
if (dx == 3)
{
return pR3;
}
}
J'ai un warning a la compilation me disant que je ne retourn pas dans
tous les cas un element correct ce qui est vrai si dx vaut 4 par exemple
mais ce cas n'arivera pas : il y aura un control en amont avant
d'appeler la fonction.
(pour le warning, des réponses ont été données, c'est à vous de voir,

selon la certitude que vous avez sur la valeur de dx)

Je verrais plutôt cette fonction renvoyer une référence qu'un
pointeur.

A la lecture des données de votre problème, il me semble qu'une
fonction n'est pas nécessaire, un tableau de pointeurs suffirait (je
crois qu'il n'est pas possible en C++ de faire directement un tableau
de reférences. J'ai bon ?)

Je vous propose un truc, étant entendu que je suis une buse en C++. Si
dx est une variable membre non static, il ne faut pas déclarer
Ccpu::dToR() en static.

<dans le Ccpu.h>
#if !defined(Ccpu_H)
#define Ccpu_H

extern int R0, R1, R2, R3;

class Ccpu
{
static int * dToRArray[4];
public:
Ccpu::Ccpu() { }
static int & dToR(int dx) { return * dToRArray[dx]; }
};
int * Ccpu::dToRArray[] = { &R0, &R1, &R2, &R3 };
#endif // Ccpu_H

<Test dans le main>
#include <iostream>
#include "Ccpu.h"

int R0, R1, R2, R3;


int main()
{
R0 = 12;
R1 = 45;
R2 = 88;
R3 = 9;
Ccpu* C = new Ccpu;
std::cout << Ccpu::dToR(0) << " / "
<< Ccpu::dToR(1) << " / "
<< Ccpu::dToR(2) << " / "
<< Ccpu::dToR(3) << std::endl;
Ccpu::dToR(1) = 12345;
std::cout << Ccpu::dToR(0) << " / "
<< Ccpu::dToR(1) << " / "
<< Ccpu::dToR(2) << " / "
<< Ccpu::dToR(3) << std::endl;

return EXIT_SUCCESS;
}

--
Pierre

Avatar
kanze
Fabien LE LEZ wrote in message
news:...

On Wed, 17 Nov 2004 19:23:16 +0000, PtitMat :

J'ai un warning a la compilation me disant que je ne retourn pas dans
tous les cas un element correct ce qui est vrai si dx vaut 4 par
exemple

mais ce cas n'arivera pas



C'est prendre tes souhaits pour la réalité :-).

Tu n'as aucun moyen d'en être sûr. Le mieux est de lancer une
exception en cas de paramètre incorrect.


Si la validité du paramètre est une précondition, une assertion vaut
mieux qu'une exception.

Ma question est donc la suivant serait-il mieux d'enchainer les if
dans des else avec quelque chose du style


C'est illisible, donc forcément moins bien.

Mais bon, pourquoi faire compliqué ? Un switch conviendrait tout aussi
bien. De plus, si ta fonction renvoie forcément un pointeur valide,
autant renvoyer une référence à la place :

int& Ccpu::dToR (int dx)
{
switch (dx)
{
case 0: return *pR0;
case 1: return *pR1;
case 2: return *pR2;
case 3: return *pR3;
default: throw std::domain_error
("Ccpu::dToR : paramètre invalide");
}
}

Mais bon, de toutes façons cette fonction devrait AMHA être inutile.
Tu as plusieurs pointeurs ayant presque le même nom ; un tableau (
std::vector<int*> ) conviendrait a priori bien mieux.


En fait, c'est un des cas où je me servirais d'un tableau à la C :

int&
Ccpu::dToR( int dx )
{
static int* const table[] { &pR0, &pR1, &pR2, &pR3, } ;
assert( dx >= 0 && dx < sizeof( table ) /sizeof( table[ 0 ] ) ) ;
return *table[ dx ] ;
}

C'est la syntaxe de l'initialisation, ici, qui fait prévaloir T[] sur
vector<T>.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Fabien LE LEZ
On 18 Nov 2004 02:01:50 -0800, :

int&
Ccpu::dToR( int dx )
{
static int* const table[] > { &pR0, &pR1, &pR2, &pR3, } ;


Je pensais mettre le tableau comme membre de la classe, au lieu de se
traîner les pR0, etc. dans toutes les fonctions membres.


--
;-)

Avatar
Fabien LE LEZ
On 18 Nov 2004 02:01:50 -0800, :

Tu n'as aucun moyen d'en être sûr. Le mieux est de lancer une
exception en cas de paramètre incorrect.


Si la validité du paramètre est une précondition, une assertion vaut
mieux qu'une exception.


Mouais...
D'un autre côté, si jamais la condition s'avère fausse dans un cas
tordu et qu'on ne s'en rend compte que chez le client, j'aime autant
qu'il ait un message "Telle fonctionnalité a échoué" avec la
possibilité de rester dans le programme et de sauvegarder ses données,
plutôt qu'un message "assert() failed at line..., vous venez juste de
perdre vos modifications".


--
;-)


Avatar
drkm
Fabien LE LEZ writes:

On 18 Nov 2004 02:01:50 -0800, :

int&
Ccpu::dToR( int dx )
{
static int* const table[] >> { &pR0, &pR1, &pR2, &pR3, } ;


Je pensais mettre le tableau comme membre de la classe, au lieu de se
traîner les pR0, etc. dans toutes les fonctions membres.


Pourquoi ? Puisque toutes les fonctions membres utilisent
`dToR()' ... ;-)

--drkm


Avatar
Fabien LE LEZ
On Thu, 18 Nov 2004 19:20:54 +0100, drkm :

Pourquoi ? Puisque toutes les fonctions membres utilisent
`dToR()' ... ;-)


Dans ce cas, ces données devraient être dans une classe à part, ne
contenant pas grand-chose d'autre que les pRx et dToR().


--
;-)

Avatar
drkm
Pierre Maurette writes:

class Ccpu
{
static int * dToRArray[4];
public:
Ccpu::Ccpu() { }
static int & dToR(int dx) { return * dToRArray[dx]; }
};
int * Ccpu::dToRArray[] = { &R0, &R1, &R2, &R3 };


J'aurais tendence à procéder plutôt comme James. De manière à
encapsuler l'accès aux éléments, même vis-à-vis des fonctions membres.

Et également à ne pas préciser la taille du tableau dans la
définition de la classe. Et à changer `Ccpu::Ccpu()' en `Ccpu()' ;-).

--drkm

Avatar
Pierre Maurette
drkm a écrit:

Pierre Maurette writes:

class Ccpu
{
static int * dToRArray[4];
public:
Ccpu::Ccpu() { }
static int & dToR(int dx) { return * dToRArray[dx]; }
};
int * Ccpu::dToRArray[] = { &R0, &R1, &R2, &R3 };


J'aurais tendence à procéder plutôt comme James.
Moi aussi. Je pense généralement gagner beaucoup à la lecture de ses

contributions. Mais ...
Je me suis forcé à poster un bout de code, en précisant "étant entendu
que je suis une buse en C++", dans cet étrange langage.
Regardez la chronologie des messages, il me semble qu'il était urgent
de sortir des pointeurs en retour, if() et autres switch()...case.

De manière à
encapsuler l'accès aux éléments, même vis-à-vis des fonctions membres.
Peut-être, et si tel est le cas, j'en retirerai la substantifique

moelle. Pour l'instant, il n'y a pas unanimité des avis compétents. Je
ne connais pas grand-chose du contexte. Pour l'instant, les "éléments"
semblent initialisés à partir de variables globales, le fait que le
tableau de ces éléments constituent une donnée membre privée de la
classe ne me choque pas.

Et également à ne pas préciser la taille du tableau dans la
définition de la classe.
Effectivement. Je peux ne pas la préciser, comme je peux la préciser.


Et à changer `Ccpu::Ccpu()' en `Ccpu()' ;-).
Oui.

--
Pierre


1 2 3 4 5