OVH Cloud OVH Cloud

[débutant objet] tableau de pointeur de fonction

47 réponses
Avatar
cyrcocq
Bonjour,

Je définis une classe qui me permet de générer des formes d'ondes (extraits
code en fin de message(toutes critiques bienvenues ;-) ))
Dans cette classe, j'ai quelques fonctions,
de type
void F0 (double);

Je veux faire un tableau de pointeurs de fonctions mais je rencontre
quelques difficultés.

J'essaie de faire un
typedef void (*fptronde) (double);

puis je fais des

fonction[0]=&onde::F0;

Mais ça ne compile pas... error C2440: '=' : impossible de convertir de
'void (__thiscall onde::* )(double)' en 'onde::fptronde'

Allors 3 questions:

comment dois-je définir mon type de pointeur sur fonction???

suis-je obligé de passer par un type que je n'utilise qu'une fois?

Les données étant dupliquées pour chaque instance de la classe, que dois-je
faire pour n'avoir qu'une instance de ce pointeur de fonction?

Merci.






class onde
{
private:

double Amp, *PAmp, Freq, *PFreq, Deph, *PDeph, Off, *POff; //Amplitude
Frequence déphasage offset (numériques fixes) et pointeurs associés.

typedef void (*fptronde) (double);

fptronde fonction [5];

void sinusoide(double);
void carree (double);
void triangulaire (double);
void montante (double);
void descendante (double);

public:

int type; //type de générateur d'onde
double sortie;
bool zero;

onde();
double CalcSortie(double);
void SetAmplitude(double);
void LieAmplitude(double&);
void SetFrequence(double);
void LieFrequence(double&);
void SetDephasage(double);
void LieDephasage(double&);
void SetOffset(double);
void LieOffset(double&);
};

void onde::sinusoide(double t)
{
sortie= *PAmp * sin( *PFreq * t + *PDeph ) + *POff;
}
(...)
onde::onde()
{
Amp=Freq=Deph=Off=0;
PAmp=&Amp;
PFreq=&Freq;
PDeph=&Deph;
POff=&Off;
type=0;

fonction[0]=&onde::sinusoide;
fonction[1]=&onde::carree;
fonction[2]=&onde::triangulaire;
fonction[3]=&onde::montante;
fonction[4]=&onde::descendante;
}

double onde::CalcSortie(double t)
{
double temp=*PFreq * t;
(*(fonction[type]))(t);
}

void onde::SetAmplitude(double A)
{
Amp=A;
PAmp=&Amp;
}
void onde::LieAmplitude(double &A)
{
PAmp=&A;
}
(...)

10 réponses

1 2 3 4 5
Avatar
Fabien LE LEZ
On Fri, 23 Sep 2005 23:55:25 +0200, "cyrcocq" :

J'espère que ce message ne sera pas trop déplacé, je reprends ici un peu ce
que Kanze


Note en passant que son prénom est James. Je ne sais pas pourquoi il a
disparu soudainement (en même temps que la majuscule du nom de famille
d'ailleurs)...

J'ai peur d'être un peu juste... peut être que je devrais revenir au C sur
une idée aussi folle?


Non. Le C++ est prévu pour être aussi rapide que possible, et pour que
les fonctionnalités que tu n'utilises pas, ne freinent pas ton
programme.

Exemple : l'appel à une fonction virtuelle peut être plus lent que
l'appel à une fonction normale. Mais si une classe donnée n'a pas de
fonctions virtuelles, l'appel à une de ses fonctions est aussi rapide
que l'appel à une fonction C.

D'autre part, les ordinateurs actuels sont très puissants. Regarder
une vidéo de 1024x756[*] pixels en 24 frames/seconde n'est plus
vraiment un problème -- et ça représente une bonne trentaine de
millions d'octets traités chaque seconde.

Est-ce que tous les calculs nécessaires dans ton programme sont assez
pour justifier des optimisations ? Je ne sais pas. Programme
proprement, en utilisant les méthodes les plus claires possibles,
compile le programme, et vois s'il tourne assez vite.


[*] Je parle bien d'une vidéo de 1024x756 pixels, pas d'une vidéo plus
petite mais étirée à la taille de l'écran par la carte vidéo...


double Amp, *PAmp, Freq, *PFreq, Deph, *PDeph, Off, *POff;


C'est de très mauvais style de mettre plus d'une définition par
instruction.


Ah? Pourtant dans le principe de ce que j'avais fait au début, je trouvais
ça abordable, pas lourd...


Ben... En pratique, c'est difficile à lire et difficile à maintenir.
En plus tu mélanges des variables de types différents (des nombres et
des pointeurs).

Si des variables sont plus ou moins indépendantes, il n'y a aucune
raison de les déclarer sur la même ligne.

Et si elles sont fortement liées, il y a des chances pour qu'on soit
vite amené à les regrouper dans une classe, i.e. transformer

class C
{
...
int x_origine, y_origine;
...
};

en :

struct /*ou class*/ Point
{
int x;
int y;
};

class C
{
...
Point origine;
...
};

Bon... et bien je vais envisager l'achat de cet ouvrage (ou d'un autre, je
vais quand même aller faire un tour sur les différentes FAQ C++ du net)


"Thinking in C++" m'a paru pas trop mal lors d'une lecture rapide.
http://www.mindview.net/Books/DownloadSites


Mais j'ai lu qu'un membre d'une classe ne pouvait s'adresser directement


Uh ?

Les pointeurs sur fonctions membres sont une saleté à utiliser, mais
c'est pas bien grave parce qu'on ne les utilise que très rarement.

Les fonctions membres en elles-mêmes, de même que les variables
membres, s'utilisent tout à fait normalement.

Ce qui suit est de style assez mauvais, mais illustre les différentes
possibilités d'accès :


struct C
{
int entier;
int *ptr_entier;

void f();

static void g()
{
// entier= 42; /* Erreur : entier est membre non-static, et g()
est static, donc indépendante de tout objet -- "this" n'existe pas. */
}

void h()
{
entier= 2;
ptr_entier= &entier;
f();
g();
}
};

int main()
{
C c;
int machin;

c.entier= 4;
c.ptr_entier= &machin;
c.ptr_entier= & c.entier;

c.f();
c.g();
C::g(); // car C::g() est static
}



Avatar
Fabien LE LEZ
On Sat, 24 Sep 2005 01:21:15 +0200, Fabien LE LEZ
:

static void g()
{
// entier= 42; /* Erreur : entier est membre non-static, et g()
est static, donc indépendante de tout objet -- "this" n'existe pas. */
}


Et bien entendu, ce code ne compile pas.
Encore une "subtilité" du C++...

Avatar
Stan
"Fabien LE LEZ" a écrit dans le message de news:

On Fri, 23 Sep 2005 23:55:25 +0200, "cyrcocq" :


D'autre part, les ordinateurs actuels sont très puissants. Regarder
une vidéo de 1024x756[*] pixels en 24 frames/seconde n'est plus
vraiment un problème -- et ça représente une bonne trentaine de
millions d'octets traités chaque seconde.

[...]



[*] Je parle bien d'une vidéo de 1024x756 pixels, pas d'une vidéo plus
petite mais étirée à la taille de l'écran par la carte vidéo...



Ce n'est pas comparable.
Aujourd'hui, les flux vidéo / audio sont géré par le DMA et traité par
des DSP ou autres circuits spécialisés.
La différence avec un traitement par le processeur lui même est
sans commune mesure. Ce dernier délègue tout le boulot...
De plus, les drivers sont rarement écrits en C++ ;-)

--
-Stan

Avatar
cyrcocq
"Fabien LE LEZ" a écrit dans le message de news:

On Fri, 23 Sep 2005 23:55:25 +0200, "cyrcocq" :
(...)


Est-ce que tous les calculs nécessaires dans ton programme sont assez
pour justifier des optimisations ? Je ne sais pas. Programme
proprement, en utilisant les méthodes les plus claires possibles,
compile le programme, et vois s'il tourne assez vite.


C'est ce que je vais faire dés que j'aurais compris comment interfacer mon
programme avec ma sortie sons!
(J'utilise Windows et c'est obscur ça aussi)

Bon... et bien je vais envisager l'achat de cet ouvrage (ou d'un autre, je
vais quand même aller faire un tour sur les différentes FAQ C++ du net)



D'un autre coté, ce goup est quand même vachtement éducatif!!!
P'tet que j'ai pas besoin de bouquin (avec le reste du net en plus...)

"Thinking in C++" m'a paru pas trop mal lors d'une lecture rapide.
http://www.mindview.net/Books/DownloadSites
Kanze>"Accelerated C++: Practical Programming by Example", de Koenig et Moo


Les Faq ne m'en ont pas dit plus les critiques sont rares...
J'vais reflechir encore un peu... Bon d'un autre cotén c'est maintenant que
j'ai du temp, faut pas que je tarde...


Mais j'ai lu qu'un membre d'une classe ne pouvait s'adresser directement


Uh ?


Hum... Encore dans le cours de Christian Casteyde...
Donc pas de soucis pour établir des pointeurs vers des doubles membre de
classes... Cool.

Stan> De plus, les drivers sont rarement écrits en C++ ;-)

Est ce donc significatif d'un probléme de perf?

C'est vrai quand même que les principes du C++ paraissent plus éloignés de
l'assembleur que ceux du C quand même!


Désolé pour mon style de réponse regroupant des réponses à plusieurs
messages, si ça ne dérange personne, moi ça m'arange...
Sachez que par ailleurs je considére toutes vos contributions avec le
respect qu'elles méritent!


Avatar
Fabien LE LEZ
On Sat, 24 Sep 2005 09:59:32 +0200, "Stan" :

Aujourd'hui, les flux vidéo / audio sont géré [...]
La différence avec un traitement par le processeur lui même est
sans commune mesure. Ce dernier délègue tout le boulot...
De plus, les drivers sont rarement écrits en C++ ;-)


Mais la vidéo, faut la décompresser. J'utilise ffdshow pour ça,
lui-même basé sur ffmpeg, bibliothèque de décompression MPEG-4 écrite
en C (Elle pourrait par conséquent être écrite en C++ sans perte de
vitesse.)

Cette biblothèque écrite en C me "pond" 32 bits pour chaque paquet de
2 pixels, soit 1024 x 756 x 24 /2 = presque 9 millions d'entiers sur
32 bits, ou 35 millions d'octets, chaque seconde. Ces octets, qui se
trouvent en RAM, sont transmis en mémoire vidéo par le bus AGP, puis
la carte vidéo en fait ce qu'elle veut.

Le tout demande environ 25 % de la puissance de mon processeur.

Avatar
Fabien LE LEZ
On Sat, 24 Sep 2005 12:04:17 +0200, "cyrcocq" :

C'est ce que je vais faire dés que j'aurais compris comment interfacer mon
programme avec ma sortie sons!
(J'utilise Windows et c'est obscur ça aussi)


Je confirme que c'est méchamment tordu. Les fonctions bas-niveau de
MCI, tout comme DirectSound, ont des interfaces passablement pénibles.
Si tu veux des conseils ou des bouts de code, n'hésite pas à me
contacter par email (avec une vraie adresse de réponse bien sûr).

Avatar
James Kanze
Fabien LE LEZ wrote:
On 23 Sep 2005 01:31:23 -0700, "kanze" :


std::vector <fptronde> fonction;





Bon, je vois... ici ca rigole pas ;-)




C'est une réaction « knee jerk ».



Non, une solution inadaptée à un problème réel.



class onde
{
[...]
fptronde fonction [5];



Cette dernière ligne est AMHA fausse.
S'il faut un tableau non-"static const", qu'on remplit dans le
constructeur, la réponse est std::vector<>.


Tout à fait, surtout quand il s'agit d'un tableau membre (mais
même là, il y a des exceptions). Mais quand on lit son posting
entier, c'est clair qu'il veut un tableau statique (et
probablement const).

Évidemment, en régardant de façon plus générale, on conclut
aussi que de toute façon, ce n'est probablement pas un tableau
de pointeurs à fonction membres dont il a besoin:-).

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34




Avatar
James Kanze
cyrcocq wrote:
J'espère que ce message ne sera pas trop déplacé, je reprends
ici un peu ce que Kanze a dit sur différents messages du fil.


Les citations seront donc complètement désordonnées et sorties
de leur contexte de départ...


C'est une bonne demarche pour récentrer.

J'ai étudié les proposition de Stan Kanze et Fabien. J'ai du y
perdre quelques cheveux. Mais entre cet exercices de
compréhension et les différents messages du fil, j'ai vraiment
beaucoup appris!!!


Probablement plus encore avec celle de kanze et ces virtual
multiples !


C'est un peu l'excès ; un bon exemple de ce qu'on peut faire,
dans certains cas. (J'ai même eu une occasion où c'était
réelement la solution idéale. Une fois en quinze ans de C++.)

Dans ton cas : je commencerais par laisser tomber l'alternatif
entre le paramètre par valeur et le paramètre lien à une autre
variable. Quitte à le réintroduire plus tard, une fois tout
marche, si le besoin se fait sentir. Mais c'est mieux de ne
s'attaquer qu'à une chose à la fois.

Non, c'est chouette d'apprendre à faire et aussi d'apprendre
à faire correctement, mais j'avoue que là j'ai un peu peur de
pas tout maîtriser.




Qu'est-ce que tu vas dire de ma solution, alors, avec des
mix-ins ? :-)



la fonction virtuelle dans la classe de base. (En passant, ça
s'appelle le concept de modèle, ou « template design pattern »



J'ai quand même fini par comprendre... en gros!


D'ici à ce que je me mette à utiliser ces principes va falloir
que je m'entraine un moment!


Il y a une certaine importance, je crois, de pouvoir distinguer
entre le principe de base et sa réalisation en C++ ; c'est
surtout pour ça que j'ai introduit les noms des modèles de
conception.

En fait, et en faisant abstraction de la question de
pointeur/valeur des paramètres (amplitude, etc.), ce que tu
veux, c'est une façon générique de calculer un onde, que
l'utilisateur n'a pas besoin de savoir la forme de l'onde.

Pour réaliser ce genre de généricité, il existe trois idiomes
(modèles de conception) assez répandus : le modèle de stratégie,
ou la délégation, le modèle template (ou la customisation par
héritage), et la généricité résolue au moment de la compilation.

Quand on fait du C++, en général, on commence par faire la
conception, et c'est au moment de la conception qu'on va choisir
entre ces trois solutions, avant de s'occuper trop aux détails
de l'implémentation. Seulement, évidemment, on fait son choix en
savant ce qui nous attend -- même sans entrer dans les détails,
on sait à peu près les difficultés qu'on risque de rencontrer
avec chaque solution, et on fait son choix en connaissance de
cause.

Quant à la question : comment faire quand on n'a pas encore
d'expérience réele avec les trois techniques, c'est plus
difficile. En fait, c'est peut-être une question de quelle
technique on veut apprendre d'abord. Mais je ne sais pas
exactement ; j'ai beaucoup de mal à me mettre dans une telle
position.

Ce qui me parait le plus hallucinant là dedans c'est que ça
donne l'impression de remonter l'édifice par la fin!


Non. On commence par la conception. Ou plutôt, on commence par
spécifier (au moins dans sa tête) exactement où il faut arriver,
qu'est-ce que doit faire le logiciel. Si, par exemple, je sais
qu'il va falloir changer la forme du courbe à la volée, sur un
objet donné, ça limite mes choix.

On part d'un objet qui quasi vide et c'est en les spécialisant
qu'on le remplit. Moi ça me tue!


On part d'une spécification de ce que doit faire l'objet. Sans
trop se poser la question comment. C'est ce qu'on appelle une
interface.

Sauf que par la suite, il a bien dit qu'il n'en voulait qu'une
copie en tout, et non une copie par objet. Et d'après ce qu'il
nous a montré, le tableau pourrait bien être const. Donc :



static fptronde const fonction[] ;



Exact sur toute la ligne


Ceci dit, on pourrait très bien considérer le concept de
stratégie, avec un objet fonctionnel comme délégué. Mais
attention alors au vocabulaire -- ce n'est pas l'objet
fonctionnel de la STL, qui lui, sera copié par valeur, et dont
le type réel est résolu à la compilation.



En fait, tout dépend de la souplesse dont tu as besoin. S'il
faut changer dynamiquement le type de fonction d'un objet
existant, il n'y a que le concept de stratégie -- la
délégation -- qui le permet.



Hum... la faudrait qu'on m'explique parce que justement c'est
là que toutes les solutions proposées coincent...


Oui, j'ai mon générateur d'onde qui change de forme d'onde.


Donc si je comprend bien, je devrais avoir 2 objets indépendants.


L'un s'occupant des paramètres (amplitude fréquence...)


L'autre de la génération de l'onde.


Tout à fait.

Note qu'en fait, tu n'en es pas loin. Si à la place de ton
variable « type » de type int, tu te servais d'un pointeur à
fonction membre, la seule différence réele par rapport au modèle
de stratégie, c'est que tu utilises une fonction membre plutôt
qu'un objet pour implémenter la stratégie.

Dans la pratique, je crois que tu trouveras l'utilisation d'un
objet plus simple à écrire, à comprendre et à maintenir.

Donc, quelque chose du genre :

class Onde
{
public:
struct Parameters
{
double amplitude ;
double frequence ;
double dephasage ;
double offset ;
} ;
struct Operator
{
virtual ~Operator() {}
virtual double operator()(
double t,
Parameters const& params ) const = 0 ;
} ;

void setType( TypeId newType )
{
myOp = getOpForType( newType ) ;
}

double operator()( double t ) const
{
return (*myOp)( t, myParams ) ;
}
// ...
} ;

Éventuellement, tu ajouteras des getters à Parameters. Comme ça,
si plus tard tu veux étendre l'implémentation pour supporter un
binding avec de variables externe, tu n'auras pas à toucher à
tes Operator.

Si j'ai bien compris, même en le disant ça me parait
abordable!


Si le type fonctionnel de l'objet reste constant, mais il
n'est déterminé que lors de la création de l'objet, le concept
de modèle me semble la solution la plus simple (mais la
délégation reste aussi possible). Et enfin, si on peut
permettre de fixer le type fonctionnel lors de la compilation,
on pourrait s'en tirer avec des templates et des objets
fonctionnels à la STL. On perd énormément de souplesse, mais
on gagne nettement en vitesse. (La différence n'est
normalement pas sensible, mais tes fonctions on l'air d'être
bien petites... et du genre qu'on risque de les appeler des
millions de fois.)



Effectivement, les appels seront fréquents nombreux...


Je cherche à générer et jouer en direct des ondes sonores
liées les unes aux autres.


(offset de l'une pouvant être la sortie d'une autre, 2 pouvant
partager un paramètre...)


Ces ondes pouvant changer d'enveloppe (de fonction)
soudainement...


Bref, je vais avoir besoin de calculer la sortie de tous mes
générateurs d'ondes (et de mes effets ultérieurement) environs
44100 fois par secondes...


Je crois que tu ne te rends pas compte de la vitesse des
processeurs modernes. Générer l'audio en temps réel ne leur pose
aucun problème. (S'il s'agissait du video, je ne dirais pas.)

J'ai peur d'être un peu juste... peut être que je devrais
revenir au C sur une idée aussi folle?


Je ne crois pas. En fait, si la vitesse devient un problème (ce
qui ne serait pas le cas ici), la solution serait de mettre la
boucle dans la fonction virtuelle. Que tu appelles une fonction
indirectement, comme on C, ou que le compilateur le fait pour
toi, parce que la fonction est virtuelle, le temps d'exécution
est à peu près le même. Et en général inférieurs aux autres
solutions qui comportent la sélection de l'opération dans la
boucle.

En général, les pointeurs à des fonctions membre est considéré
un topique avancé.



En fait c'est cet aspect vitesse qui m'a fait imaginer une
telle méthode. (Je n'ai toujours pas complètement abandonné
l'idée!)


Parce que les pointeurs à des fonctions membres sont peu
courant, la plupart des compilateurs n'ont pas investi beaucoup
de moyens à les rendre rapide.

double Amp, *PAmp, Freq, *PFreq, Deph, *PDeph, Off, *POff;
//Amplitude
Frequence déphasage offset (numériques fixes) et pointeurs associés.




C'est de très mauvais style de mettre plus d'une définition
par instruction.



Ah? Pourtant dans le principe de ce que j'avais fait au début,
je trouvais ça abordable, pas lourd...


Ça prète beaucoup à la confusion, et ça rend la maintenance
délicate.

Ça fait un moment que je n'ai pas regardé un livre C++ pour
débutants, mais d'après tout ce que j'entends, "Accelerated
C++: Practical Programming by Example", de Koenig et Moo,
serait parmi les meilleurs, sinon le meilleur.



Bon... et bien je vais envisager l'achat de cet ouvrage (ou
d'un autre, je vais quand même aller faire un tour sur les
différentes FAQ C++ du net)


Dommage, je suis actuellement chercheur d'emploi (Ingénieur
systèmes et réseau Microsoft en recherche dans la moitié Sud
ou La moitié Ouest de la France...) et mes moyens sont
limités!


Sinon, une nouvelle question sur la validité de mon code de
départ a surgi suite à mes approfondissements du jour


J'avais dans ma classe des données membres Amp et des *PAmp.


Dans une fonction je faisais un PAmp=&Amp


Mais j'ai lu qu'un membre d'une classe ne pouvait s'adresser
directement


Un membre donné oui. Tu ne peux pas prendre l'adresse d'une
fonction membre comme ça, mais il n'y a aucun problème avec un
membre donnée.

Note que le résultat n'est *pas* un pointeur à membre. C'est un
pointeur à un double, tout court. Qui est un membre, peut-être,
mais pour l'utilisateur, c'est un double, ni plus ni moins,
accessible à travers le pointeur sans avoir connaissance de
l'objet qui le contient.

Ce qui signifierai que si j'avais une classe "onde" avec un
membre "double amp"


Je ne pouvais pointer dessus qu'avec un "double onde::*" et
non avec un "double *"


Tu peux en faire les deux. Avec une signification différente :

double* pd = &amp, ou &(this->amp)
prend l'adresse du double particulier qui est membre de
l'objet actuel. N'inporte qui, même en dehors de
l'objet, pourrait ensuite faire *pd, sans connaître
l'objet.

double (Onde::*pd) = &Onde::amp
prend l'« address du membre », qui n'est pas réelement
une adresse, mais une recette pour accéder à l'élément,
étant donné un objet. Pour l'utiliser ce pointeur, il
faut bien avoir un objet de type Onde, ou un pointeur à
un tel objet, pour pouvoir faire this->*pd, unOnde.*pd,
etc.

Ici, je crois que c'est le premier que tu veux.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34



Avatar
Fabien LE LEZ
On Fri, 23 Sep 2005 23:55:25 +0200, "cyrcocq" :

peut être que je devrais revenir au C sur
une idée aussi folle?


En fait, il n'est pas impossible que tu soit tenté de "revenir", non
pas au C, mais à l'assembleur.

En effet, son ton processeur, un int fait 32 bits, et tu donnes des
entiers de 16 bits à ta carte son.

Or, le C comme le C++ sont incapables de faire des calculs
arithmétiques sur des entiers autres que des "int", ces cons !

Du coup, quand on veut travailler sur des entiers de 8 bits (pixels)
ou 16 bits (échantillons de son), ben... on est dans la merde :-(

Une des rares limitations du C++ à justifier l'usage de
l'assembleur...

Avatar
kanze
Fabien LE LEZ wrote:
On Fri, 23 Sep 2005 23:55:25 +0200, "cyrcocq" :

peut être que je devrais revenir au C sur une idée aussi
folle?


En fait, il n'est pas impossible que tu soit tenté de
"revenir", non pas au C, mais à l'assembleur.

En effet, son ton processeur, un int fait 32 bits, et tu
donnes des entiers de 16 bits à ta carte son.

Or, le C comme le C++ sont incapables de faire des calculs
arithmétiques sur des entiers autres que des "int", ces cons !

Du coup, quand on veut travailler sur des entiers de 8 bits
(pixels) ou 16 bits (échantillons de son), ben... on est dans
la merde :-(


Sur une machine moderne ? Qu'est-ce que ça change ? Si tu as des
16 bits en entré, et tu affectes le résultat à une variable de
16 bits, c'est exactement le même résultat que si tu fais tous
les calculs en 16 bits. (Sauf peut-être dans certains cas de
débordement. Mais alors, ton code a un comportement indéfini.)

--
James Kanze GABI Software
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


1 2 3 4 5