Olena/ImageTypes

From LRDE

reda 25 nov. 2002

dans olena il existe deux macros pour determiner le type exacte d'un objet :

1- Exact : si j'ai bien compris, cette macro est utilisée pour tous les types sauf les images.

2- Concrete : c'est pour les images

si c'est correct ce que je dis, pourquoi les noms des deux macros sont tellement differents pourquoi pas :

Exact et Exact_Image

ou

Concrete et Concrete_image

pollux 7 jan 2003

Je n'ai pas vu de réponse précise à cette question.

Concrete ne sert effectivement que pour les images. Exact peut aussi être utilisé sur les images.

Exact retourne le type exact d'une image au sens hiérarchique, c'est-à-dire le type qui a servi à instancier l'image.


Concrete retourne un type d'image concret, c'est-à-dire possédant de vrais pixels modifiables, avec les même caractéristiques de dimension et type de valeurs que l'argument.

Suppose qu'un algorithme reçoive en argument un type d'image qui soit en fait une fonction : aucune valeur n'est stockée, et les valeurs demandée sont calculées à la volée. Le type exact est celui qui à servit à déclarer l'image, par exemple FunctionalImage2D<int_u8, myFunctor>.

Un tel type ne peut être utilisé qu'en lecture. Si l'algorithme doit créer une nouvelle image à partir de cet argument, il ne peut utiliser ce type exact. À la place, il doit créer une image de type Image2d<int_u8>. C'est précisément ce que retourne Concrete : un type d'image similaire, mais modifiable.

Dans Olena, Concrete est (ou était, je ne suis plus très à la page) donc utilisé pour tous les types de retour et les variables temporaires lorsqu'elles sont modifiées, tandis qu' Exact est utilisé (souvent à travers Exact_ref our Exact_cref), pour caster les arguments en entrée.

Si je me souviens bien, c'est Théo qui m'avait fait introduire cette différence.

david 16 fev 2003

Commencons avec le plus simple : le comportement de Exact(I). Je me base sur la dernière version d'Olena, 8.3 (prcs).


 oln/core/type.hh 
1 # define Exact(Type) \
2 typename type::exact<Type>::ret

Exact est donc un outil générique (dans le sens ou il marche sur la hiérarchie d'images, mais pas que), pour dérouler les hiérarchies statiques (récursives), et obtenir le type finalement instancié.

Maintenant, regardons le comportement de Concrete(I).

 oln/core/image.hh 
1 # define Concrete(ImgType)			\
2 typename mute<ImgType>::ret

Concrete(I) lance le trait global mute<I> avec un seul paramètre. Plus haut, dans le même fichier, on trouve:

1 // mute
2 
3   template<class I, class T = typename type::exact<I>::ret::value>
4   struct mute
5   {
6 	 typedef typename type::exact<I>::ret::mute<T>::ret ret;
7   };

Le trait mute global prend normalement deux paramètres:

  • I=: un type d'image (ex: =image2d<T1>), qui est constitué d'un méta-type (image2d<??>), et d'un type de données T1 (qui vient boucher le trou du "??" précédent).
  • T=: un type de données, donné dans le but de remplacer le =T1 précédent.

mute<I, T> a donc pour vocation de générer un nouveau type d'image ou l'on a remplacer le type de données initial (T1) par T, dans le type d'image I. On obtient ainsi image2d<T> a la place de I valant image2d<T1> dans notre exemple précédent. Dans notre cas d'utilisation avec Concrete, un seul argument est passé. Le deuxième (T) est donc celui par défaut. Cet argument par défaut est typename type::exact<I>::ret::value.

Celui-ci est obtenu en lançant le trait exact sur I= (résultat idem que Exact(I) ), et en accédant au typedef =value. value correspond au type de données de l'image (types Olena: int_u8, etc...). On obtient donc comme deuxième paramètre du mute le type de données du type exact du premier paramètre =I=. (finalement, aucun changement de type de données).

Le trait mute définit un type ret comme résultat du trait mute cette fois-ci interne au type exact de I (type::exact<I>::ret). Cet "inner-trait" est lancé sur T=, valant, comme nous l'avons vu, le même type de données que celui de =type::exact<I>::ret.

Maintenant, pour obtenir le type résultat de notre Concrete(I), il faut voir le comportement du "inner-trait" mute de I. Un exemple, pour les images 2d

 oln/core/image2d.hh 
 1 template<class T, class Inferior = type::bottom>
 2   class image2d : public internal::_real_image2d< T, image2d<T,Inferior> >
 3   {
 4 	//....
 5 	template<class U>
 6 	 struct mute
 7 	 {
 8 		typedef image2d<U> ret;
 9 	 };
10   };

A ma connaissance, dans toutes les classes d'images instanciables actuelles, cet "inner-trait" mute a le même comportement: permettre de définir un nouveau type d'image en changeant le type de données interne dans un méta-type d'image donné. i. e.: passer de image2d<T1> à image2d<T2>.

Le seul effet de notre Concrete(I) lancé sur une image<image2d<int_u8> > par exemple sera de descendre au type concret image2d<int_u8>, puisque le mute de image2d ne change pas le meta-type image2d<???>, et que Concrete(image2d<int_u8>) lance précisement image2d<int_u8>::mute<int_u8> (pas de changement du type de données).

Conclusion perso

A ma connaisssance, avec les classes actuelles d'images, Concrete(I) et Exact(I) ont exactement le même comportement. Concrete(I) n'est utilisable effectivement qu'avec les images, puisque seules les classes d'images semblent disposer d'inner-traits mute.

Avec le comportement des inner mute des classes d'images actuelles, Concrete(I) revient donc à une torture pure et simple du compilateur, là où un Exact(I) autrement plus direct aurait suffi.

Par contre, pour rejoindre Pollux (et Theo ?), on peut imaginer des classes d'images dont le inner mute changerait le méta-type d'image (peut-être existent-elles déjà, mais je ne les ai pas trouvées). Par exemple : FunctionalImage2d (cf exemples de Pollux), pourrait avoir un mute ainsi défini:

 1 template<class T>
 2 class FunctionalImage2d : // pouet
 3 {
 4 	//....
 5 	template<class U>
 6 	 struct mute
 7 	 {
 8 		typedef image2d<U> ret;
 9 	 };
10 };

Là, l'utilisation de Concrete(I) au lieu de Exact(I) aurait un sens (à chaque fois que l'on veut un type d'image avec des vrais pixels). C'est d'ailleurs surement cela que sous-entendent Pollux et Theo.

Post-Conclusion

Remarque judicieuse de Nicolas Burrus: il semble sémantiquement douteux que le "inner" mute d'une FunctionalImage2d definisse autre chose qu'un FunctionalImage2d<U>. Cela pourrait en effet gener le comportement d'algos utilisant directement mute dans le sens courant.

Concrete(I) n'aurait donc aucune utilité sous sa forme actuelle ???


En même temps, mute ayant généralement pour vocation sous-jacente une modification de typage de données, on peut se questionner sur sa sémantique dans le cas d'éventuelles images "read-only" comme l'exemple FunctionalImage2d. Dans ce cas, ne veut-on pas justement un type d'images "read/write" en retour du "inner" mute ?

Seul les concepteurs de cette séparation Concrete/Exact sauront répondre, ou tout du moins exposer leur but premier.