[C++/résolu] operator [][] () et types variants

() et types variants [C++/résolu] operator [][] - C++ - Programmation

Marsh Posté le 18-03-2006 à 17:32:21    

Salut les C++iens!
 
Alors voilà, je me demandais quelle méthode utiliser en C++ pour surcharger l'opérateur [][]: oui vous ne voyez pas double  :pt1cable: , il ne s'agit pas de [] mais de [][].
 
Quelque chose comme:
Tableau2D tab(10,10);
tab[2][1]=0;
 
Y a t-il une solution efficace?  :??:
 
solution 1 => un proxy: sucharger[] retourner un objet avec [] surchargé
solution 2 => surcharger plutôt operator()
 
Deuxièmement:
Comment faire retourner à l'opérateur un objet variant de plusieurs types convertissables entre eux?  :heink: (variant ~=~ union)
 
* Solutions résolvables à la compilation:
solution 1 => utiliser un template, et l'instancier avec toutes les conversions possibles. mots clés: Boost.Variant+Alexandrescu
solution 2 => plus facile à coder, plus lent: avoir un type de plus grande précision utilisé pour convertir de/vers les autres type. ex.: type string.
 
* Solutions complémentaires, résolvables à l'execution:
solution 1 => pour un nombre restreint de types variants: un switch,
solution 2 => pur C++, pour de gros objets: une classe conteneur d'un pointeur vers une classe abstraite pure. mots-clés ci-dessous: ++fab+SignalType
solution 3 => plus efficace sur des petits objets: une classe faisant appel à un tableau statique à 2 dimensions de pointeurs sur fonctions de conversion. mots clés ci-dessous: conversions+mytype


Message édité par nargy le 24-03-2006 à 00:04:25
Reply

Marsh Posté le 18-03-2006 à 17:32:21   

Reply

Marsh Posté le 18-03-2006 à 18:03:04    

tu surcharges operator[] et tu lui fait retourné un objet pour lequel tu peut appliquer []
 
dans le cas d'une matrice l'appel du premier [] retourne l'adresse de la ligne sur laquel on peut indexer la colonne voulue
 

Code :
  1. template<typename T>
  2. class matrix
  3. {
  4. public:
  5. typedef std::size_t size_type;
  6. typedef T value_type;
  7. typedef value_type * pointer;
  8. typedef const value_type * const_pointer;
  9. ...
  10. private:
  11. size_type lines, columns;
  12. pointer p;
  13. ...
  14. public:
  15. ...
  16. const_pointer operator [] (size_type n) const
  17. {
  18. return p + n * columns;
  19. }
  20. };


 
mais bon, ca existe deja  

Reply

Marsh Posté le 18-03-2006 à 18:27:45    

skelter> merci
 
>mais bon, ca existe deja
 
En fait j ai un cas pratique, dans lequel j ai des classes polymorphes, auquelles il serait utile de fournir cet opérateur, mais les classes ont des tableaux différents en taille, en type, en nombre de dimensions. Parfois elles n ont pas du tout de tableaux (dans ce cas l opérateur renvoie une constante).

Reply

Marsh Posté le 19-03-2006 à 04:04:27    

Bon, apparemment y a pas de solution simple.
Ça va être:
virtual const Val& get(uint, uint) const;
et:
virtual void set(uint, uint, const Val& );
 
à la bonne franquette!

Reply

Marsh Posté le 19-03-2006 à 10:07:32    

et pitié, au minimum mettez des vector<> derrière

Reply

Marsh Posté le 19-03-2006 à 10:19:10    

haha, merci du conseil taz, mais je me suis fait mon propre autoptr et autoarray. Comme je le disais, ça va être à la bonne fanquette: programmé avec les doigts!

Reply

Marsh Posté le 19-03-2006 à 10:47:51    

sans doute complètement pourri donc ...

Reply

Marsh Posté le 19-03-2006 à 11:51:09    

Taz a écrit :

et pitié, au minimum mettez des vector<> derrière


 
oui, ou valarray
 

Citation :


Bon, apparemment y a pas de solution simple.
Ça va être:
virtual const Val& get(uint, uint) const;
et:
virtual void set(uint, uint, const Val& );
 
à la bonne franquette!


 
ca marche ce que je t'ai montré et c'est simple
get/set c'est nul à utiliser (surtout le set avec la valeur en 3e parametre), tu pourrais également surcharger operator(), ca se fait aussi

Reply

Marsh Posté le 19-03-2006 à 12:31:59    

> sans doute complètement pourri donc ...
non...!? j ai pas l impression d avoir besoin de vector. juste d allouer des blocs de mémoire partagés et détachables. j ai même pas d objets là dedans, c est des images.
 
les blocs mémoire peuvent par ailleurs être gérés par le hardware ou partagés en réseau.
 
je ne me sent pas assez confortable avec std:: pour trouver les bonnes classes pour faire ça.
 
> oui, ou valarray  
ça peut être interessant
 
> get/set c'est nul à utiliser
ouais!
 
> surcharger operator()
bonne idée!

Reply

Marsh Posté le 19-03-2006 à 14:11:47    

Citation :

non...!? j ai pas l impression d avoir besoin de vector. juste d allouer des blocs de mémoire partagés et détachables. j ai même pas d objets là dedans, c est des images.
 
les blocs mémoire peuvent par ailleurs être gérés par le hardware ou partagés en réseau.
 
je ne me sent pas assez confortable avec std:: pour trouver les bonnes classes pour faire ça.


 
essaies quand meme avec std::vector ou autre et montre le code si il y a un probleme

Reply

Marsh Posté le 19-03-2006 à 14:11:47   

Reply

Marsh Posté le 19-03-2006 à 15:41:24    

C est possible de faire travailler std::vector sur de la mémoire déjà allouée via mmap()?

Reply

Marsh Posté le 19-03-2006 à 15:48:28    

std::vector est parametrable au niveau de l'allocateur, ca permet pas mal de chose
 
mais je comprends pas pourquoi tu voudrais que ton vector utilises de la mémoire allouée par mmap, ca n'a pas de sens ? tu veux faire quoi ?

Reply

Marsh Posté le 19-03-2006 à 16:14:49    

Le but, c est d arriver à faire dériver plusieurs classes de façon à pouvoir plugger entre eux des flux vidéos. Des flux vidéo sources:
- webcam (mmap)
- tv in (mmap mjpeg temps réel)
- fichier vidéo (décompression en mémoire),
des flux cibles:
- librairie graphique (soft)
- buffer carte vidéo (hard)
- overlay carte vidéo (hard)
- carte compression vidéo (hard)
- tv out (hard)
- fichier vidéo (soft)
 
Pour l instant, je me suis dit que pour factoriser au maximum les classes polymorphes, j aurai besoin d un gestion mémoire particulière qui puisse à la fois gérer un flux vidéo hardware en provenance d une webcam (souvent mmap) ou à destination d une carte video (buffer video), et avoir possibilité de détacher ou partager ces tampons mémoire pour des filtres software (resize notamment).

Reply

Marsh Posté le 19-03-2006 à 23:11:43    

Taz avait présenté une solution exploitant un proxy dans une autre file. Ca s'applique ici aussi.

Reply

Marsh Posté le 20-03-2006 à 14:18:27    

http://forum.hardware.fr/hardwaref [...] 0942-1.htm
 
ouah je suis bon moi :) je m'en souvenais même plus.

Reply

Marsh Posté le 20-03-2006 à 15:46:26    

Heureusement que je suis là pour te rappeler ton génie.  :o

Reply

Marsh Posté le 20-03-2006 à 20:02:17    

Salut les génies :D
 
Bon, alors je me suis décidé pour utiliser l opérateur () (uint,uint), histoire d éviter à avoir à coder deux fois plus de classes polymorphes.
 
Pour std::vector, j ai étudié le doc publié par gnu gcc, très instructif les allocators. Mais je vais essayer de le placer là où il faut.
 
J ai d autres problèmes:
 
1°) mon compilateur préféré (gcc) aligne mes classes sur des mots: 1, 2 ou 4 octets, ce qui n est pas du tout du goût de ma webcam qui utilise une array de 3 octets par pixels.
 
2°) je suis un peu perdu dans les variants (il me semble que ça s appelle comme ça).
J encapsule les différents formats de pixels (ex: class GRAY/RGBA) dans une classe abstraite Signal pour pouvoir faire:
cartevideo(x,y)=webcam(x,y);
(rem: ça doit pas être trop optimisé tout ça)
 
un peu de code:

Code :
  1. class Signal
  2. {
  3.   protected:
  4.   static RGBA convertGRAY2RGBA(const GRAY v) { return v; }
  5.   static GRAY convertRGBA2GRAY(const RGBA v) { return v; }
  6.   public:
  7.   virtual Signal& operator=(RGBA)=0;
  8.   virtual Signal& operator=(GRAY)=0;
  9.   virtual operator RGBA () const =0;
  10.   virtual operator GRAY () const =0;
  11. };
  12. class SignalGRAY: public Signal
  13. {
  14.   GRAY& s;
  15.   public:
  16.   SignalGRAY(GRAY& v): s(v) {}
  17.   Signal& operator=(const Signal& o) { s=o; return *this; }
  18.   Signal& operator=(RGBA v) { s=convertRGBA2GRAY(v); return *this; }
  19.   Signal& operator=(GRAY v) { s=v; return *this; }
  20.   operator RGBA () const { return convertGRAY2RGBA(s); }
  21.   operator GRAY () const { return s; }
  22. };
  23. class Plot
  24. {
  25.   public:
  26.   virtual Signal& operator () (unsigned int, unsigned int)=0;
  27. };
  28. class PlotGray: public Plot
  29. {
  30.   GRAY _color;
  31.   public:
  32.   PlotGray(): _color(0) {}
  33.   Signal& operator () (unsigned int, unsigned int)
  34.   { return SignalGRAY(_color); }
  35. };


 
>  Signal& operator () (unsigned int, unsigned int)
>  { return SignalGRAY(_color); }
Ça c est une grosse connerie:

Code :
  1. plot_gray.h:19: error: invalid initialization of non-const reference of type 'Signal&'
  2. from a temporary of type 'SignalGRAY'


 
J ai comme l impression que je fait fausse route, non?

Message cité 1 fois
Message édité par nargy le 20-03-2006 à 20:03:13
Reply

Marsh Posté le 20-03-2006 à 20:30:02    

nargy a écrit :


>  Signal& operator () (unsigned int, unsigned int)
>  { return SignalGRAY(_color); }
Ça c est une grosse connerie:

Code :
  1. plot_gray.h:19: error: invalid initialization of non-const reference of type 'Signal&'
  2. from a temporary of type 'SignalGRAY'


 
J ai comme l impression que je fait fausse route, non?


 
N-ième fois : Assigner un temporaire à une référence non const, c'est illégal.  
Assigner un temporaire à une référence constante, c'est légal mais *dans ton cas* stupide car le temporaire va cesser de vivre à la fin de sa scope.

Reply

Marsh Posté le 20-03-2006 à 20:44:43    

oui, j ai bien précisé:
> Ça c est une grosse connerie:  
 
Seulement si je fait:
>  Signal operator () (unsigned int, unsigned int)
>  { return SignalGRAY(_color); }
 
c en est une autre: ma classe Signal possède des fonctions virtuelles pures.
 
je sèche sur la solution...

Reply

Marsh Posté le 20-03-2006 à 21:29:19    

ha oauis, je fait fausse route, les variants se font avec des templates. je sens que ça va se terminer avec un gros switch ou une array de pointeurs de fonctions.

Reply

Marsh Posté le 20-03-2006 à 22:19:26    

#
virtual Signal& operator=(RGBA)=0;
#
 virtual Signal& operator=(GRAY)=0;
#
 virtual operator RGBA () const =0;
#
 virtual operator GRAY () const =0;
 
 
 
tu crois faire quoi là ?

Reply

Marsh Posté le 20-03-2006 à 23:05:25    

convertir différents formats de pixels entre eux...

Reply

Marsh Posté le 20-03-2006 à 23:11:18    

Enfin, ce sont des fonctions viruelle pures, réimplémentées dans SignalGRAY, par ex..
 
Ainsi, quand j ai une classe qui dérive de Signal, je peut convertir la valeur représenté de et vers n importe quelle autre palette.
 
Puis je pensais utiliser celà pour écrire:
cartevideo(x,y)=webcam(x,y);
 
Mais je me rends compte que pour utiliser les fonctions virtuelles, je dois à un moment donné avoir un pointeur (ou une ref) sur l objet signal.

Reply

Marsh Posté le 20-03-2006 à 23:22:54    

ha ouais j ai pigé ce que tu voulais dire:
> tu crois faire quoi là ?
- j initialise un objet signal en mettant ces fonctions à nul.
 
Pour utiliser cette méthode, j en suis à <<où trouver un pointeur sur l objet SignalGray?>>
 
Je peut toujours le créer avec un new ou un delete, mais ça implique une allocation mémoire à gérer, et ça commence à faire beaucoup de pointeurs.edit:
 
edit:
 
j ai un peu googlé et je n ai trouvé que deux moyens de faire des objets ``variant``:
- un gros switch pabo,
- des templates qui se résolvent à la compilation.


Message édité par nargy le 20-03-2006 à 23:36:42
Reply

Marsh Posté le 21-03-2006 à 10:23:14    

[:drapal]

Reply

Marsh Posté le 21-03-2006 à 11:43:20    

> [:drapal]
c est une blague, ou le sujet t interesse?
 
j ai renommé ma classe signal en basesignal, et j ai maintenant une classe signal qui contient un pointeur sur un new basesignal et des constructeurs/destructeurs & opérateurs =. ça compile, mais j ai pas encore testé. je le sens pas.
 
je teste demain, si ça marche, je publie un code propre.
 
> je peut toujours le créer avec un new ou un delete, mais ça implique une allocation mémoire à gérer, et ça commence à faire beaucoup de pointeurs.
- utiliser un pool? std::__pool_allocator?

Reply

Marsh Posté le 21-03-2006 à 11:46:09    

J'aime bien avoir des cas d'utilisation.

Reply

Marsh Posté le 21-03-2006 à 18:23:06    

<Note>
Je cherche à connecter dans un premier temps 1 webcam, 1 carte tv-in, mixer le tout et envoyer le résultat sur 1 carte video-overlay et 1 carte vidéo-buffer via réseau. Je ne connais pas à l avance les palettes de couleur, les tailles et le fps des entrées-sorties vidéo. Chaque périphérique a sa gestion propre de son espace mémoire (sauf le réseau où j ai le choix).
</Note>
 
Ok, j ai élagé un max le code.
 
Normalement, je gère dans des fichiers à part les définitions/conversions de couleurs. J ai aussi un fichier pour les types qui dépendent du CPU (tailles/endienness).
 
Dans ce morceau je n ai gardé que 2 types de palettes graphiques: RGBA (4 channels=32bits), GRAY (1 channel=8bits), et un type NumericSignal (1 channel=flottant) (pour tester).
 
Mes ulucubrations de vocabulaire:
Signal = pixel,
Plot = réseau, mémoire ou périphérique vidéo.
 

Code :
  1. // code tout pourri: voir plus loin
  2. ////////////////////////// grosse classe signal
  3. class Signal
  4. {
  5.   // classe de base abtraite
  6.   class BaseSignal
  7.   {
  8. ..................
  9.     public:
  10.     virtual BaseSignal& operator=(const BaseSignal& o)=0;
  11.     virtual BaseSignal& operator=(RGBA)=0;
  12.     virtual BaseSignal& operator=(GRAY)=0;
  13.     virtual BaseSignal& operator=(NumericSignal)=0;
  14.     virtual operator RGBA () const =0;
  15.     virtual operator GRAY () const =0;
  16.     virtual operator NumericSignal () const =0;
  17.   };
  18.   class SignalRGBA: public BaseSignal
  19.   {
  20. ..................
  21.   };
  22.   class SignalGRAY: public BaseSignal
  23.   {
  24. ..................
  25.   };
  26.   class SignalSignal: public BaseSignal
  27.   {
  28. ..................
  29.   };
  30.   // utiliser un signal générique
  31.   BaseSignal* _s;
  32.   public:
  33.   // constructeurs/destructeur
  34.   Signal(NumericSignal& s): _s(new SignalSignal(s)) {}
  35.   Signal(GRAY& s): _s(new SignalGRAY(s)) {}
  36.   Signal(RGBA& s): _s(new SignalRGBA(s)) {}
  37.   ~Signal() { delete _s; }
  38.   // affectation
  39.   Signal& operator=(const Signal& o)
  40.   {
  41. fprintf(stderr,"Signal::=(Signal)\n" );
  42.     *_s=*(o._s); return *this; }
  43. ..................
  44. };
  45. ..................
  46. //////////////////////// petit test
  47. int main()
  48. {
  49.   PlotColor t1(0xffff00);
  50.   PlotSignal t2(0.25L);
  51.   PlotGray t3(0x7f);
  52.   PlotColor t4(0x00ffff);
  53.   PlotSignal t5(0.75L);
  54.   t1.dump();
  55.   t2.dump();
  56.   t3.dump();
  57.   t4.dump();
  58.   t5.dump();
  59.   t5(0,0)=t4(0,0)=t3(0,0)=t2(0,0)=t1(0,0);
  60.   t1.dump();
  61.   t2.dump();
  62.   t3.dump();
  63.   t4.dump();
  64.   t5.dump();
  65.   return 0;
  66. }


 
Ok, et ça marche :) :
 

Code :
  1. color: <255, 255, 0>
  2. numeric:0.25
  3. gray:127
  4. color: <255, 0, 255>
  5. numeric:0.75
  6. Signal::=(Signal)
  7. Signal::=(Signal)
  8. Signal::=(Signal)
  9. Signal::=(Signal)
  10. color: <255, 255, 0>
  11. numeric:0.666667
  12. gray:170
  13. color: <170, 170, 170>
  14. numeric:0.666667


 
Reste à faire quelques tests de rapidité, puis reproduire la méthode pour les périphériques pour pouvoir écrire directement quelquechose comme:

Code :
  1. VideoIn tv("/dev/video0" );
  2. WebCam webcam("/dev/video1" );
  3. VideoOverlay ecran( (XVideoPort) 61 );
  4. VideoBroadcast broadcast("localhost", 10107);
  5. while(1)
  6. {
  7.   Video temp=tv+webcam;
  8.   broadcast=temp;
  9.   ecran=temp;
  10. }


Message édité par nargy le 22-03-2006 à 14:40:37
Reply

Marsh Posté le 22-03-2006 à 11:58:26    

> utiliser un pool? std::__pool_allocator?
 
Avec du debug sur les allocations du test précédent:

Code :
  1. new sig 0x804dc78
  2. new sig 0x804dc88
  3. new sig 0x804dc98
  4. new sig 0x804dca8
  5. del sig 0x804dca8
  6. del sig 0x804dc98
  7. del sig 0x804dc88
  8. del sig 0x804dc78


 
indique qu une pile est plus apropriée.

Reply

Marsh Posté le 22-03-2006 à 14:36:59    

> J ai comme l impression que je fait fausse route, non?
 
oui: total space cette méthode de classe de variants avec fonctions virtuelles
 
j ai recodé en utilisant un tableau statique à deux dimensions avec des pointeurs sur les fonctions de convertion et une classe signal contenant:
  void* pval;
  unsigned int mytype;
  Signal& operator=(const Signal& o)
  {
    (conversions[mytype][o.mytype])(pval,o.pval);
    return *this;
  }
 
au lieu d un pointeur sur BaseSignal,
 
comparaison sur 30 millions d affectations: edit:

Code :
  1. sans optim               avec optim
  2. classes virtuelles : 0:09.47elapsed 93%CPU   0:06.25elapsed 95%CPU
  3. array de pfonctions: 0:03.46elapsed 96%CPU   0:01.26elapsed 90%CPU


C est pas très joli les void*, et c est presque plus du C++, mais il y a un facteur 2,6 à 5,2.

Message cité 1 fois
Message édité par nargy le 22-03-2006 à 15:09:05
Reply

Marsh Posté le 22-03-2006 à 19:44:24    

nargy a écrit :

oui: total space cette méthode de classe de variants avec fonctions virtuelles


Comme tu dis. ça ne correspond en rien avec ce que j'ai l'habitude non seulement d'écrire, mais de voir. Déjà, variant, je ne sais pas d'où tu sors ce terme, mais je n'ai pas l'impression que tu te référes aux variants (à la Alexandrescu) que la littérature évoque -- et que Boost.Variant implémente, notament.
Après, j'ai du mal avec l'opertor= virtuel ...
 
Vu que tu as des conversion à faire, j'aurai utiliser les constructeurs pour convertir dans un sens, et les Proxy pour convertir dans l'autre sens.
 
 

Citation :

j ai recodé en utilisant un tableau statique à deux dimensions avec des pointeurs sur les fonctions de convertion et une classe signal contenant:
  void* pval;
  unsigned int mytype;
  Signal& operator=(const Signal& o)
  {
    (conversions[mytype][o.mytype])(pval,o.pval);
    return *this;
  }


 
Je ne comprends pas tout ce que tu essayes de faire, mais pour éviter le void*, et faire des conversions efficaces, quelquechose dans cet esprit doit pouvoir marcher :
 

Code :
  1. template<>
  2. struct Conversion<Signal,OtherSignal> // specialisation d'une classe template Conversion
  3. {
  4.     static Signal::valueType convert( Sig2 const& sig2 )
  5.         { .... }
  6. };
  7. template <class OtherSig>
  8. Signal( OtherSig const& sigP )
  9.     : sig_( Conversion<Signal,OtherSig>::convert( sigP ) )
  10. {}


 

Citation :

comparaison sur 30 millions d affectations: edit:

Code :
  1. sans optim               avec optim
  2. classes virtuelles : 0:09.47elapsed 93%CPU   0:06.25elapsed 95%CPU
  3. array de pfonctions: 0:03.46elapsed 96%CPU   0:01.26elapsed 90%CPU



Est-ce que ce test reflète parfaitement l'utilisation que tu en as ? Est-ce qu'un profiler ne serait pas plus précis pour mesurer les performances de ce code dans le contexte de ton application ?
 

Citation :

C est pas très joli les void*, et c est presque plus du C++


Ah

Reply

Marsh Posté le 22-03-2006 à 19:46:18    

nargy a écrit :

[snip]
indique qu une pile est plus apropriée.


 
Pourquoi ?

Reply

Marsh Posté le 22-03-2006 à 21:18:46    

++fab> merci de tees comments
 
> variant
- ouais, c est pas tout à fait ça, ça y ressemble vaguement, dans le sens où c est une classe qui est une union de plusieurs types.
 
> Après, j'ai du mal avec l'opertor= virtuel
- j en avais peut être pas besoin d ailleurs du virtuel
 
> pour éviter le void*, et faire des conversions efficaces, quelquechose dans cet esprit doit pouvoir marcher ...
- ouais la version template c est à peu ça, pour N types à convertir entre eux, il y a N*N fonctions de conversions (N=33).
 
> Est-ce que ce test reflète parfaitement l'utilisation que tu en as ?
- non, c est dans le pire des cas.
 
> C est pas très joli les void*, et c est presque plus du C++
- ...mais du C
 
> indique qu une pile est plus apropriée.
- parceque les allocations/désallocations de Signal ne se croisent pas (même si elles se croisaient la pile resterait dans des limites raisonnables), et une pile est ce qu il y a de plus rapide.
 
> Je ne comprends pas tout ce que tu essayes de faire
 
Ce que j essaye de faire: relier des périphériques vidéos.
 
Pour prendre un schema très simple: afficher la vidéo d une webcam sur un écran.
 
La webcam est gérée avec video4linux. Video4linux me donne un pointeur sur une zone mémoire contenant l image courante. Video4linux m informe aussi du codage de couleurs: Gray8 ou Bgr24.
 
Pour faire simple, l écran est géré par le serveur X. Le serveur me donne un pointeur vers la mémoire vidéo et m informe de la palette utilisée: Rgb24 ou Rgb32.
 
La webcam et l écran peuvent changer de palette, j ai donc besoin de fonctions de conversions de palette de la forme:
 
convertir(adressevideo* dest, adressevideo* source);
 
... pour toutes les combinaisons de palettes necessaires.
 
J ai déjà écrit un programme d affichage de webcam en C avec de gros switch et une interface C++, résultat: c est lent (sauts de frames) et pas pratique à réutiliser/modifier.
 
J ai divers morceaux de code pour faire pareil avec ma carte télé et une carte vidéo overlay. J ai plusieurs filtres vidéo aussi: écran bleu, détecteur de mouvement.
 
Je cherche à réunir ces morceaux de codes de façon homogène et réutilisable simplement, comme par exemple:
ecran(x,y)=webcam(x,y);
...sans se soucier des palettes à l extérieur des objets ``ecran`` et ``webcam``.

Reply

Marsh Posté le 22-03-2006 à 21:32:35    

ha oui:
> convertir(adressevideo* dest, adressevideo* source);
>  ... pour toutes les combinaisons de palettes necessaires.  
 
D où mon idée de faire un tableau de N*N cases avec les fonctions de conversion dedans.
 
D où ces étranges lignes de code:
  void* pval;
  unsigned int mytype;
  Signal& operator=(const Signal& o)
  {
    (conversions[mytype][o.mytype])(pval,o.pval);
    return *this;
  }

Reply

Marsh Posté le 22-03-2006 à 22:00:39    

Citation :

> variant
- ouais, c est pas tout à fait ça, ça y ressemble vaguement, dans le sens où c est une classe qui est une union de plusieurs types.


L'implémentation d'un variant est tellement traumatisante, que ton code aurait du m'y faire penser :)
 

Citation :

> Après, j'ai du mal avec l'opertor= virtuel
- j en avais peut être pas besoin d ailleurs du virtuel


Considère la solution Proxy pour émuler la surcharge du type de retour, et constructeur multiple peut-etre ?
 

Citation :

> pour éviter le void*, et faire des conversions efficaces, quelquechose dans cet esprit doit pouvoir marcher ...
- ouais la version template c est à peu ça, pour N types à convertir entre eux, il y a N*N fonctions de conversions (N=33).


Et comment tu regle ce coup avec ton tableau de pointeur de fonction ?
 

Citation :

> C est pas très joli les void*, et c est presque plus du C++
- ...mais du C


Ma remarque n'allait pas dans ce sens ...
 

Citation :

> indique qu une pile est plus apropriée.
- parceque les allocations/désallocations de Signal ne se croisent pas (même si elles se croisaient la pile resterait dans des limites raisonnables), et une pile est ce qu il y a de plus rapide.


Rien compris. Je n'arrive pas à comprendre comment tu peux présupposer du comportement de l'allocateur standard.
Mais si tu as le choix, et qu'une classe de stockage automatique fait l'affaire (C++ ne connait pas la pile), ne t'en prives pas.
 
 

Citation :

Je cherche à réunir ces morceaux de codes de façon homogène et réutilisable simplement, comme par exemple:
ecran(x,y)=webcam(x,y);
...sans se soucier des palettes à l extérieur des objets ``ecran`` et ``webcam``.


J'arrive pas à saisir la sémantique de ton operator=. ça veut dire "Chaque pixel de la webcam se retrouve sur l'écran" ?
 
Ce ne serait pas plus élégant de l'exprimer comme ça :

Code :
  1. Ecran ecran;
  2. Webcam webcam;
  3. ecran << webcam;
  4. // ou webcam >> ecran;


Message édité par ++fab le 22-03-2006 à 23:11:42
Reply

Marsh Posté le 22-03-2006 à 23:28:27    

> Et comment tu regle ce coup avec ton tableau de pointeur de fonction ?
 
un peu de code, ça sera plus simple:

Code :
  1. // les fonctions de conversion:
  2. void convertGRAY2RGBA(void* pval, const void* p)
  3. { *((RGBA*)pval)=*((GRAY*)p); }
  4. void convertRGBA2GRAY(void* pval, const void* p)
  5. { *((GRAY*)pval)=*((RGBA*)p); }
  6. // etc.... de toute palette vers toute palette, ici 3*3
  7. // le type ``pointeur sur fonction de conversion``
  8. typedef void (*SignalConversion)(void*, const void*);
  9. // tableau statique de 3*3 fonctions de conversions
  10. // 0=GRAY, 1=RGBA, 2=Numeric
  11. // ex.: pour convertir de GRAY à RGBA:
  12. //    la fonction se trouve dans conversions[1][0]
  13. extern SignalConversion conversions[3][3];
  14. class Signal // la classe qui gère le niveau pixel
  15. {
  16.   void* pval; // pointeur vers la donnée
  17.   unsigned int mytype; // type de la donnée
  18.   public:
  19. // constructeurs
  20.   Signal(GRAY& s): pval(&s), mytype(0) {}
  21.   Signal(RGBA& s): pval(&s), mytype(1) {}
  22.   Signal(NumericSignal& s): pval(&s), mytype(2) {}
  23. // conversion générique
  24.   Signal& operator=(const Signal& o)
  25.   { (conversions[mytype][o.mytype])(pval,o.pval); return *this; }
  26. // etc...
  27. };


 
> ça veut dire "Chaque pixel de la télé se retrouve sur l'écran" ?
- juste le pixel situé à la position (x,y)
 

Code :
  1. Ecran ecran;
  2.     Webcam webcam;
  3.     ecran << webcam;


Oui, ça c est pour gérer le niveau flux vidéo. Mais avant d en arriver là, je doit gérer le niveau pixel: palettes de couleurs variables.
 
Après il y a le niveau image: tailles variables.
 
Enfin le niveau du flux vidéo: fps différents et entrelacements différents.
 
Dans l idéal, j ai des fonctions de conversion spécialisée vers tous les types possibles de périphériques avec toutes les tailles et tous les formats de pixel. Mais avant d en arriver là:

Code :
  1. for(unsigned int x=0;x<tailleX;x++)
  2.   for(unsigned int y=0;y<tailleY;y++)
  3.     ecran(x,y)=webcam(x,y);


...la copie pixel par pixel est obligatoire.

Reply

Marsh Posté le 23-03-2006 à 00:24:55    

Je vois 2 autres techniques :
1 - modèle stratégie
 

Code :
  1. class Signal
  2. {
  3. public:
  4.     Signal( SignalType const& sig ) : sig_( sig.clone() ) {}
  5.     SignalType signal() const { return *sig_; }
  6. private:
  7.     SignalType* sig_;
  8.     Signal& operator=( Signal const& sig )
  9.     { sig_->convert( sig.signal() ); return *this; }
  10. };
  11. class SignalType
  12. {
  13. public:
  14.     void convert( SignalType const& s) { doConvert( s ); }
  15.     Signal* clone() const { return doClone(); }
  16.     virtual ~SignalType() {}
  17. private:
  18.     virtual Signal* doClone() const = 0;
  19.     virtual void doConvert( SignalType const& ) = 0;
  20. };
  21. class RGBA : public SignalType { ... };


 
2- classe template
 

Code :
  1. template <class SigT>
  2. class Signal
  3. {
  4.      SigT sig_;
  5. public:
  6.      SigT signal() const { return sig_; }
  7.      Signal( SigT const& sig )
  8.           : sig_( sig ) {}
  9.    
  10.      template <class SigT2>
  11.      Signal& operator=( Signal<SigT2> const& sig )
  12.           { sig_ = Converter<SigT,SigT2>::convert( sig.signal() ); return *this; }
  13. };


 
avec Converter comme décrit plus haut.
 
1- est souple à l'exécution.
2- n'est souple qu'à la compilation, mais est plus efficace. La, tu peux t'attendre à ce que le compilateur développe en ligne tout les appels.


Message édité par ++fab le 23-03-2006 à 00:42:11
Reply

Marsh Posté le 23-03-2006 à 01:07:45    

Ha oui, je te suis. Mais, il y a un gros mais...
 
D abord en ce qui concerne la méthode 2, les templates se résolvent à la compilation, or il est tout à fait possible que le format de pixel change pendant l execution. Par exemple, en utilisant xrandr sur le serveur X.
 
 
Ensuite pour la méthode 2:
 
J'ai testé deux programmes, l'un avec des fonctions virtuelles, l'autre avec des pointeurs sur fonction.
Les pointeurs sur fonction sont 2 fois plus rapides. J'ai trouvé l'explication suivante:
 
objet -> vtable -> pfonction -> code
 
ça fait minimum 3 déréférences pour appeler une fonction virutelle, et:
 
objet -> pfonction -> code
 
donc minimum 2 déréférences avec les pointeurs sur fonction, or en assembleur i586+:
 
C = nbre cycles pour 1 déréférence = nbre cycles pour 2 déréférences
 
donc:
nbre cycles 3 déréférences = 2*C
 
=> les fonctions virtuelle sont au moins deux fois plus lentes que des pointeurs sur fonction.
 
Quand je fait:

Code :
  1. (conversions[mytype][o.mytype])(pval,o.pval);


j utilise 1 déférence pour le tableau, 1 pour le pointeur de fonction. L opérateur = lui même est en inline.
 
La méthode que j ai exposé plus haut ressemble à la tienne, à celà près que je mettais la classe SignalType en sous classe de Signal. Sinon du point de vue performances c est la même chose, le code assembleur doit être très proche sinon identique.
 
Je peut envisager la méthode 1 pour le niveau image, ou flux vidéo. Pour le niveau pixel je suis obligé de privilégier la performance. Pour une image 1MégaPixel les fonctions virtuelle me limitent à 8 fps et avec pointeur sur fonction 30 fps.
 
Ok, je te remercie, il me semble avoir fait le tour des méthodes pour avoir disons des classes convertibles entre elles.
 
Je vais me mettre la méthode des templates sous le coude pour les flux vidéo avec palette fixe.
 
Si tu aimes te prendre la tête sur les templates récursifs, une implémentation de variant que j ai étudié (j ai pas encore tout pigé...):
http://www.codeproject.com/cpp/union_list.asp

Reply

Marsh Posté le 23-03-2006 à 01:10:47    

la difference entre une fonction virtuelle et un pointeur de fonction m'echappe pas mal

Reply

Marsh Posté le 23-03-2006 à 01:19:04    

elles te permettent toutes les deux d effectuer un code différent en fonction d un type d objet, à l execution.

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed