J'ai le modèle suivant :
Classe de base : Object
class Object
{
public :
typedef enum {
MEM_USER,
MEM_DSP,
}TYPE_Mem;
typedef enum {
OBJ_IMAGE,
OBJ_VECTOR,
OBJ_SCALAR,
}TYPE_Object;
typedef enum {
DATA_U8,
DATA_S8,
DATA_U16,
DATA_S16,
DATA_U32,
DATA_S32,
DATA_FLOAT,
}TYPE_Data;
Object165(
const TYPE_Mem typeMem,
const TYPE_Object typeObj,
const TYPE_Data typeData);
TYPE_Object GetTypeObject () const {return _typeObj;}
TYPE_Mem GetTypeMem () const {return _typeMem;}
TYPE_Data GetTypeData () const {return _typeData;}
u32* GetMemU32 () { return _mem; }
void SetMemU32 (u32* mem) { _mem = mem; }
// impossible de dériver cette méthode à cause du type de retour !
//virtual u32* GetMem () { return _mem; }
private :
TYPE_Mem _typeMem;
TYPE_Object _typeObj;
TYPE_Data _typeData;
u32 *_mem;
};
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
Dans Object j'ai donc un pointeur sur la zone mémoire (u32
*_mem) mais ce pointeur est en 32 bits.
Or les calculs peuvent être en 8 bits par exemple.
Je cherche donc une solution pour retrouver soit un pointeur
sur la classe de base,
soit un pointeur sur la zone mémoire de
base dans le bon type (<Type*> _mem).
Par exemple, je crée 3 Objects :
const u32 szX = 20, szY = 12;
Object *obA = new ImageUser<u8>(szX, szY);
Object *obB = new ImageUser<u16>(szX, szY);
Object *obR = new ImageUser<s32>(szX, szY);
J'ai aussi créé une classe MathUser dans laquelle j'ai cette fonction :
void
MathUser::Add2Image(Object &obR, Object &obA, Object &obB)
{
// test de compatibilité entre obR, obA, obB : taille ....
// Si OK => calcul
for(u32 i=0; i<obR.GetSize()
{
// c'est là mon problème, je dois retrouver le type de base
// ce code ne peut pas etre compilé évidemment
obR.GetMem()[i] = obA.GetMem()[i] + obB.GetMem[i];
}
}
J'ai mis en place un système d'ID (TYPE_Mem, TYPE_Object,
TYPE_Data) qui me permet de savoir à quel type d'objet j'ai
affaire ; c'est comme un typeid()
Comment est-ce que je peux faire pour récupérer soit les
pointeurs sur ImageUser<u8> ...,
soit sur les données elles-mêmes : u8* ...
J'avais pensé à un système de MACRO pour récupérer un pointeur
du bon type (sorte de downcasting) mais ça ne marche pas car
j'en ai besoin à l'éxécution et non à la compilation !
Malheureusement je ne peux pas non plus utiliser une méthode
virtuelle du genre GetMem() qui me retourne un pointeur car
j'utilise des templates et donc il me faut <Type*>GetMem()
Est-ce qu'il y a une solution simple qui me permet de coder ce
dont j'ai besoin sans avoir à écrire toutes les combinaisons
possibles et imaginables ?
(il y a plus de 400 combinaisons rien que pour une addition et
je dois implémenter une bonne cinquantaine d'opérandes !)
J'ai le modèle suivant :
Classe de base : Object
class Object
{
public :
typedef enum {
MEM_USER,
MEM_DSP,
}TYPE_Mem;
typedef enum {
OBJ_IMAGE,
OBJ_VECTOR,
OBJ_SCALAR,
}TYPE_Object;
typedef enum {
DATA_U8,
DATA_S8,
DATA_U16,
DATA_S16,
DATA_U32,
DATA_S32,
DATA_FLOAT,
}TYPE_Data;
Object165(
const TYPE_Mem typeMem,
const TYPE_Object typeObj,
const TYPE_Data typeData);
TYPE_Object GetTypeObject () const {return _typeObj;}
TYPE_Mem GetTypeMem () const {return _typeMem;}
TYPE_Data GetTypeData () const {return _typeData;}
u32* GetMemU32 () { return _mem; }
void SetMemU32 (u32* mem) { _mem = mem; }
// impossible de dériver cette méthode à cause du type de retour !
//virtual u32* GetMem () { return _mem; }
private :
TYPE_Mem _typeMem;
TYPE_Object _typeObj;
TYPE_Data _typeData;
u32 *_mem;
};
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
Dans Object j'ai donc un pointeur sur la zone mémoire (u32
*_mem) mais ce pointeur est en 32 bits.
Or les calculs peuvent être en 8 bits par exemple.
Je cherche donc une solution pour retrouver soit un pointeur
sur la classe de base,
soit un pointeur sur la zone mémoire de
base dans le bon type (<Type*> _mem).
Par exemple, je crée 3 Objects :
const u32 szX = 20, szY = 12;
Object *obA = new ImageUser<u8>(szX, szY);
Object *obB = new ImageUser<u16>(szX, szY);
Object *obR = new ImageUser<s32>(szX, szY);
J'ai aussi créé une classe MathUser dans laquelle j'ai cette fonction :
void
MathUser::Add2Image(Object &obR, Object &obA, Object &obB)
{
// test de compatibilité entre obR, obA, obB : taille ....
// Si OK => calcul
for(u32 i=0; i<obR.GetSize()
{
// c'est là mon problème, je dois retrouver le type de base
// ce code ne peut pas etre compilé évidemment
obR.GetMem()[i] = obA.GetMem()[i] + obB.GetMem[i];
}
}
J'ai mis en place un système d'ID (TYPE_Mem, TYPE_Object,
TYPE_Data) qui me permet de savoir à quel type d'objet j'ai
affaire ; c'est comme un typeid()
Comment est-ce que je peux faire pour récupérer soit les
pointeurs sur ImageUser<u8> ...,
soit sur les données elles-mêmes : u8* ...
J'avais pensé à un système de MACRO pour récupérer un pointeur
du bon type (sorte de downcasting) mais ça ne marche pas car
j'en ai besoin à l'éxécution et non à la compilation !
Malheureusement je ne peux pas non plus utiliser une méthode
virtuelle du genre GetMem() qui me retourne un pointeur car
j'utilise des templates et donc il me faut <Type*>GetMem()
Est-ce qu'il y a une solution simple qui me permet de coder ce
dont j'ai besoin sans avoir à écrire toutes les combinaisons
possibles et imaginables ?
(il y a plus de 400 combinaisons rien que pour une addition et
je dois implémenter une bonne cinquantaine d'opérandes !)
J'ai le modèle suivant :
Classe de base : Object
class Object
{
public :
typedef enum {
MEM_USER,
MEM_DSP,
}TYPE_Mem;
typedef enum {
OBJ_IMAGE,
OBJ_VECTOR,
OBJ_SCALAR,
}TYPE_Object;
typedef enum {
DATA_U8,
DATA_S8,
DATA_U16,
DATA_S16,
DATA_U32,
DATA_S32,
DATA_FLOAT,
}TYPE_Data;
Object165(
const TYPE_Mem typeMem,
const TYPE_Object typeObj,
const TYPE_Data typeData);
TYPE_Object GetTypeObject () const {return _typeObj;}
TYPE_Mem GetTypeMem () const {return _typeMem;}
TYPE_Data GetTypeData () const {return _typeData;}
u32* GetMemU32 () { return _mem; }
void SetMemU32 (u32* mem) { _mem = mem; }
// impossible de dériver cette méthode à cause du type de retour !
//virtual u32* GetMem () { return _mem; }
private :
TYPE_Mem _typeMem;
TYPE_Object _typeObj;
TYPE_Data _typeData;
u32 *_mem;
};
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
Dans Object j'ai donc un pointeur sur la zone mémoire (u32
*_mem) mais ce pointeur est en 32 bits.
Or les calculs peuvent être en 8 bits par exemple.
Je cherche donc une solution pour retrouver soit un pointeur
sur la classe de base,
soit un pointeur sur la zone mémoire de
base dans le bon type (<Type*> _mem).
Par exemple, je crée 3 Objects :
const u32 szX = 20, szY = 12;
Object *obA = new ImageUser<u8>(szX, szY);
Object *obB = new ImageUser<u16>(szX, szY);
Object *obR = new ImageUser<s32>(szX, szY);
J'ai aussi créé une classe MathUser dans laquelle j'ai cette fonction :
void
MathUser::Add2Image(Object &obR, Object &obA, Object &obB)
{
// test de compatibilité entre obR, obA, obB : taille ....
// Si OK => calcul
for(u32 i=0; i<obR.GetSize()
{
// c'est là mon problème, je dois retrouver le type de base
// ce code ne peut pas etre compilé évidemment
obR.GetMem()[i] = obA.GetMem()[i] + obB.GetMem[i];
}
}
J'ai mis en place un système d'ID (TYPE_Mem, TYPE_Object,
TYPE_Data) qui me permet de savoir à quel type d'objet j'ai
affaire ; c'est comme un typeid()
Comment est-ce que je peux faire pour récupérer soit les
pointeurs sur ImageUser<u8> ...,
soit sur les données elles-mêmes : u8* ...
J'avais pensé à un système de MACRO pour récupérer un pointeur
du bon type (sorte de downcasting) mais ça ne marche pas car
j'en ai besoin à l'éxécution et non à la compilation !
Malheureusement je ne peux pas non plus utiliser une méthode
virtuelle du genre GetMem() qui me retourne un pointeur car
j'utilise des templates et donc il me faut <Type*>GetMem()
Est-ce qu'il y a une solution simple qui me permet de coder ce
dont j'ai besoin sans avoir à écrire toutes les combinaisons
possibles et imaginables ?
(il y a plus de 400 combinaisons rien que pour une addition et
je dois implémenter une bonne cinquantaine d'opérandes !)
sej wrote:J'ai le modèle suivant :
Je ne suis pas sûr d'avoir réelement compris ton problème, mais
plusieurs choses me viennent à l'ésprit.Classe de base : Object
class Object
{
public :
typedef enum {
MEM_USER,
MEM_DSP,
}TYPE_Mem;
Pourquoi le typedef, et non pas simplement :
enum TYPE_Mem { ... } ;
(Aussi, je trouve qu'il y a un peu trop de majuscules. J'aurais
fait quelque chose du genre :
enum MemoryType { user, dsp } ;
Et en passant, le virgule après MEM_DSP est formellement
interdit pas le langage, même si certains compilateurs
l'acceptent.)typedef enum {
OBJ_IMAGE,
OBJ_VECTOR,
OBJ_SCALAR,
}TYPE_Object;
Comme ci-dessus.typedef enum {
DATA_U8,
DATA_S8,
DATA_U16,
DATA_S16,
DATA_U32,
DATA_S32,
DATA_FLOAT,
}TYPE_Data;
Object165(
const TYPE_Mem typeMem,
const TYPE_Object typeObj,
const TYPE_Data typeData);
TYPE_Object GetTypeObject () const {return _typeObj;}
TYPE_Mem GetTypeMem () const {return _typeMem;}
TYPE_Data GetTypeData () const {return _typeData;}
u32* GetMemU32 () { return _mem; }
void SetMemU32 (u32* mem) { _mem = mem; }
// impossible de dériver cette méthode à cause du type de retour !
//virtual u32* GetMem () { return _mem; }
private :
TYPE_Mem _typeMem;
L'utilisation de _ comme préfixe peut poser de problèmes. Mieux
vaut comme suffixe (AMHA peu esthétique), ou un préfixe du genre
m_ ou my. Ou trouver de bons noms, pour que le préfixe ne soit
pas nécessaire.TYPE_Object _typeObj;
TYPE_Data _typeData;
u32 *_mem;
};
Plusieurs points :
-- Si j'ai bien compris, _mem pourrait pointer en fait à
n'importe quel type. Alors, c'est un void* qu'il faut.
-- Tu dérives de cette classe. Avec la possibilité éventuelle
de vouloir supprimer un objet alloué dynamiquement par un
pointeur à la base. Alors, un destructeur virtuel s'impose.
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
Dans Object j'ai donc un pointeur sur la zone mémoire (u32
*_mem) mais ce pointeur est en 32 bits.
Le pointeur est en 32 bits, ou il pointe à un bloc de mémoire de
32 bits.Or les calculs peuvent être en 8 bits par exemple.
Je cherche donc une solution pour retrouver soit un pointeur
sur la classe de base,
Si tu as un pointeur à la classe dérivée, il se convertit
implicitement en pointeur à la classe de base.soit un pointeur sur la zone mémoire de
base dans le bon type (<Type*> _mem).
N'importe quel T* peut se convertir en void*, et vice versa. La
convertion vers void* est même implicite ; pour faire celle de
void*, on se sert de static_cast. En principe, la conversion de
void* vers %* n'est valide que ce le void* result d'une
conversion du T*, mais dans la pratique, dans la mesure où il
s'agit des types fondamentaux, si le mémoire est correctement
alignée, ça doit aller.
N'empèche que je ne vois pas le problème que tu essaies à
résoudre.Par exemple, je crée 3 Objects :
const u32 szX = 20, szY = 12;
Object *obA = new ImageUser<u8>(szX, szY);
Object *obB = new ImageUser<u16>(szX, szY);
Object *obR = new ImageUser<s32>(szX, szY);
J'ai aussi créé une classe MathUser dans laquelle j'ai cette fonction :
void
MathUser::Add2Image(Object &obR, Object &obA, Object &obB)
{
// test de compatibilité entre obR, obA, obB : taille ....
// Si OK => calcul
for(u32 i=0; i<obR.GetSize()
{
// c'est là mon problème, je dois retrouver le type de base
// ce code ne peut pas etre compilé évidemment
obR.GetMem()[i] = obA.GetMem()[i] + obB.GetMem[i];
}
}
Le problème ici, c'est que Add2Image doit en fait être
polymorphique. (Ou un template, si on veut le typage statique.
C'est un cas où les templates sont bien plus simples à utiliser
que l'héritage.)
Enfin, je verrais quelque chose comme :
template< typename T >
class TypedObject : public Object ...
// Classe « typée » intermédiaire entre Object et
// UserObject ou DSPObject...
template< typename T >
void
TypedObject< T >::addToMe( Object const& other )
{
TypedObject const& rhs
= dynamic_cast< TypedObject const& >( other ) ;
if ( size() != other.size() ) {
throw bad_size() ;
}
for ( int i = 0 ; i < size() ; ++ i ) {
mem()[ i ] += rhs.mem()[ i ] ;
}
}
où addToMe est une fonction virtuelle pûre dans Object. (J'ai
un peu l'impression que l'utilisation même d'une classe de base
non-typée n'est pas une bonne idée, mais je ne connais pas assez
de l'application pour en être sûr.)J'ai mis en place un système d'ID (TYPE_Mem, TYPE_Object,
TYPE_Data) qui me permet de savoir à quel type d'objet j'ai
affaire ; c'est comme un typeid()
Et pourquoi pas se servir de typeid() ? Ou dynamic_cast ?Comment est-ce que je peux faire pour récupérer soit les
pointeurs sur ImageUser<u8> ...,
À partir d'Object* ? Avec dynamic_cast, évidemment.soit sur les données elles-mêmes : u8* ...
Une fois que tu as un pointeur sur la classe dérivée, la reste
doit couler de source. Mais en général, c'est mieux d'éviter le
besoin d'un pointeur sur la classe dérivée, avec des fonctions
virtuelles qui conviennent.J'avais pensé à un système de MACRO pour récupérer un pointeur
du bon type (sorte de downcasting) mais ça ne marche pas car
j'en ai besoin à l'éxécution et non à la compilation !
C'est pour ça qu'on a des fonctions virtuelles.Malheureusement je ne peux pas non plus utiliser une méthode
virtuelle du genre GetMem() qui me retourne un pointeur car
j'utilise des templates et donc il me faut <Type*>GetMem()
Le problème, c'est qu'il faudrait de toute façon une
implémentation spécifique de ton algorithme pour chaque type de
pointeur. La solution, SI on a besoin d'une résolution
dynamique, c'est une fonction virtuelle.
Note bien qu'au moins d'avoir absolument besoin d'une résolution
dynamique, la solution template est nettement supérieur ici. Tu
as en fait une précondition sur les types : dans le cas
d'addition, par exemple, que les deux tableaux soient de même
type. Et les templates sont conçus précisement pour exprimer des
prédicats sur les types.Est-ce qu'il y a une solution simple qui me permet de coder ce
dont j'ai besoin sans avoir à écrire toutes les combinaisons
possibles et imaginables ?
Avec une aiguillage dynamique, ça va être difficile. Peut-être
quelque chose du genre :
class AbstractOperator
{
public:
virtual ~AbstractOperator() {}
virtual void add2Image(
Object& dest,
Object const& op1,
Object const& op2 ) const = 0 ;
// ...
} ;
template< typename Result, typename Op1, typename Op2 >
class Operator : public AbstractOperator
{
public:
virtual void add2Image(
Object& dest,
Object const& op1,
Object const& op2 ) const
{
Result& d = dynamic_cast< Result >( dest) ;
Op1 const& o1 = dynamic_cast< Op1 >( op1 ) ;
Op2 const& o2 = dynamic_cast< Op2 >( op2 ) ;
// boucle avec calcul...
}
} ;
Ensuite, tu crées une instance (statique, probablement) de
chacun des types dérivés, et un std::map< TypeTriplet,
AbstractOperator const* >. Finalement, la fonction « générique »
créer un TypeTriplet à partir des types dynamiques de ses
paramètres, et cherche la classe opératrice voulue dans le map.(il y a plus de 400 combinaisons rien que pour une addition et
je dois implémenter une bonne cinquantaine d'opérandes !)
Un mélange de l'héritage, des templates, et une simulation de
l'aiguillage multiple pourraient le faire. N'empèche que
j'essaierais de rédéfinir le problème pour qu'il ne soit pas
nécessaire.
--
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
sej wrote:
J'ai le modèle suivant :
Je ne suis pas sûr d'avoir réelement compris ton problème, mais
plusieurs choses me viennent à l'ésprit.
Classe de base : Object
class Object
{
public :
typedef enum {
MEM_USER,
MEM_DSP,
}TYPE_Mem;
Pourquoi le typedef, et non pas simplement :
enum TYPE_Mem { ... } ;
(Aussi, je trouve qu'il y a un peu trop de majuscules. J'aurais
fait quelque chose du genre :
enum MemoryType { user, dsp } ;
Et en passant, le virgule après MEM_DSP est formellement
interdit pas le langage, même si certains compilateurs
l'acceptent.)
typedef enum {
OBJ_IMAGE,
OBJ_VECTOR,
OBJ_SCALAR,
}TYPE_Object;
Comme ci-dessus.
typedef enum {
DATA_U8,
DATA_S8,
DATA_U16,
DATA_S16,
DATA_U32,
DATA_S32,
DATA_FLOAT,
}TYPE_Data;
Object165(
const TYPE_Mem typeMem,
const TYPE_Object typeObj,
const TYPE_Data typeData);
TYPE_Object GetTypeObject () const {return _typeObj;}
TYPE_Mem GetTypeMem () const {return _typeMem;}
TYPE_Data GetTypeData () const {return _typeData;}
u32* GetMemU32 () { return _mem; }
void SetMemU32 (u32* mem) { _mem = mem; }
// impossible de dériver cette méthode à cause du type de retour !
//virtual u32* GetMem () { return _mem; }
private :
TYPE_Mem _typeMem;
L'utilisation de _ comme préfixe peut poser de problèmes. Mieux
vaut comme suffixe (AMHA peu esthétique), ou un préfixe du genre
m_ ou my. Ou trouver de bons noms, pour que le préfixe ne soit
pas nécessaire.
TYPE_Object _typeObj;
TYPE_Data _typeData;
u32 *_mem;
};
Plusieurs points :
-- Si j'ai bien compris, _mem pourrait pointer en fait à
n'importe quel type. Alors, c'est un void* qu'il faut.
-- Tu dérives de cette classe. Avec la possibilité éventuelle
de vouloir supprimer un objet alloué dynamiquement par un
pointeur à la base. Alors, un destructeur virtuel s'impose.
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
Dans Object j'ai donc un pointeur sur la zone mémoire (u32
*_mem) mais ce pointeur est en 32 bits.
Le pointeur est en 32 bits, ou il pointe à un bloc de mémoire de
32 bits.
Or les calculs peuvent être en 8 bits par exemple.
Je cherche donc une solution pour retrouver soit un pointeur
sur la classe de base,
Si tu as un pointeur à la classe dérivée, il se convertit
implicitement en pointeur à la classe de base.
soit un pointeur sur la zone mémoire de
base dans le bon type (<Type*> _mem).
N'importe quel T* peut se convertir en void*, et vice versa. La
convertion vers void* est même implicite ; pour faire celle de
void*, on se sert de static_cast. En principe, la conversion de
void* vers %* n'est valide que ce le void* result d'une
conversion du T*, mais dans la pratique, dans la mesure où il
s'agit des types fondamentaux, si le mémoire est correctement
alignée, ça doit aller.
N'empèche que je ne vois pas le problème que tu essaies à
résoudre.
Par exemple, je crée 3 Objects :
const u32 szX = 20, szY = 12;
Object *obA = new ImageUser<u8>(szX, szY);
Object *obB = new ImageUser<u16>(szX, szY);
Object *obR = new ImageUser<s32>(szX, szY);
J'ai aussi créé une classe MathUser dans laquelle j'ai cette fonction :
void
MathUser::Add2Image(Object &obR, Object &obA, Object &obB)
{
// test de compatibilité entre obR, obA, obB : taille ....
// Si OK => calcul
for(u32 i=0; i<obR.GetSize()
{
// c'est là mon problème, je dois retrouver le type de base
// ce code ne peut pas etre compilé évidemment
obR.GetMem()[i] = obA.GetMem()[i] + obB.GetMem[i];
}
}
Le problème ici, c'est que Add2Image doit en fait être
polymorphique. (Ou un template, si on veut le typage statique.
C'est un cas où les templates sont bien plus simples à utiliser
que l'héritage.)
Enfin, je verrais quelque chose comme :
template< typename T >
class TypedObject : public Object ...
// Classe « typée » intermédiaire entre Object et
// UserObject ou DSPObject...
template< typename T >
void
TypedObject< T >::addToMe( Object const& other )
{
TypedObject const& rhs
= dynamic_cast< TypedObject const& >( other ) ;
if ( size() != other.size() ) {
throw bad_size() ;
}
for ( int i = 0 ; i < size() ; ++ i ) {
mem()[ i ] += rhs.mem()[ i ] ;
}
}
où addToMe est une fonction virtuelle pûre dans Object. (J'ai
un peu l'impression que l'utilisation même d'une classe de base
non-typée n'est pas une bonne idée, mais je ne connais pas assez
de l'application pour en être sûr.)
J'ai mis en place un système d'ID (TYPE_Mem, TYPE_Object,
TYPE_Data) qui me permet de savoir à quel type d'objet j'ai
affaire ; c'est comme un typeid()
Et pourquoi pas se servir de typeid() ? Ou dynamic_cast ?
Comment est-ce que je peux faire pour récupérer soit les
pointeurs sur ImageUser<u8> ...,
À partir d'Object* ? Avec dynamic_cast, évidemment.
soit sur les données elles-mêmes : u8* ...
Une fois que tu as un pointeur sur la classe dérivée, la reste
doit couler de source. Mais en général, c'est mieux d'éviter le
besoin d'un pointeur sur la classe dérivée, avec des fonctions
virtuelles qui conviennent.
J'avais pensé à un système de MACRO pour récupérer un pointeur
du bon type (sorte de downcasting) mais ça ne marche pas car
j'en ai besoin à l'éxécution et non à la compilation !
C'est pour ça qu'on a des fonctions virtuelles.
Malheureusement je ne peux pas non plus utiliser une méthode
virtuelle du genre GetMem() qui me retourne un pointeur car
j'utilise des templates et donc il me faut <Type*>GetMem()
Le problème, c'est qu'il faudrait de toute façon une
implémentation spécifique de ton algorithme pour chaque type de
pointeur. La solution, SI on a besoin d'une résolution
dynamique, c'est une fonction virtuelle.
Note bien qu'au moins d'avoir absolument besoin d'une résolution
dynamique, la solution template est nettement supérieur ici. Tu
as en fait une précondition sur les types : dans le cas
d'addition, par exemple, que les deux tableaux soient de même
type. Et les templates sont conçus précisement pour exprimer des
prédicats sur les types.
Est-ce qu'il y a une solution simple qui me permet de coder ce
dont j'ai besoin sans avoir à écrire toutes les combinaisons
possibles et imaginables ?
Avec une aiguillage dynamique, ça va être difficile. Peut-être
quelque chose du genre :
class AbstractOperator
{
public:
virtual ~AbstractOperator() {}
virtual void add2Image(
Object& dest,
Object const& op1,
Object const& op2 ) const = 0 ;
// ...
} ;
template< typename Result, typename Op1, typename Op2 >
class Operator : public AbstractOperator
{
public:
virtual void add2Image(
Object& dest,
Object const& op1,
Object const& op2 ) const
{
Result& d = dynamic_cast< Result >( dest) ;
Op1 const& o1 = dynamic_cast< Op1 >( op1 ) ;
Op2 const& o2 = dynamic_cast< Op2 >( op2 ) ;
// boucle avec calcul...
}
} ;
Ensuite, tu crées une instance (statique, probablement) de
chacun des types dérivés, et un std::map< TypeTriplet,
AbstractOperator const* >. Finalement, la fonction « générique »
créer un TypeTriplet à partir des types dynamiques de ses
paramètres, et cherche la classe opératrice voulue dans le map.
(il y a plus de 400 combinaisons rien que pour une addition et
je dois implémenter une bonne cinquantaine d'opérandes !)
Un mélange de l'héritage, des templates, et une simulation de
l'aiguillage multiple pourraient le faire. N'empèche que
j'essaierais de rédéfinir le problème pour qu'il ne soit pas
nécessaire.
--
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
sej wrote:J'ai le modèle suivant :
Je ne suis pas sûr d'avoir réelement compris ton problème, mais
plusieurs choses me viennent à l'ésprit.Classe de base : Object
class Object
{
public :
typedef enum {
MEM_USER,
MEM_DSP,
}TYPE_Mem;
Pourquoi le typedef, et non pas simplement :
enum TYPE_Mem { ... } ;
(Aussi, je trouve qu'il y a un peu trop de majuscules. J'aurais
fait quelque chose du genre :
enum MemoryType { user, dsp } ;
Et en passant, le virgule après MEM_DSP est formellement
interdit pas le langage, même si certains compilateurs
l'acceptent.)typedef enum {
OBJ_IMAGE,
OBJ_VECTOR,
OBJ_SCALAR,
}TYPE_Object;
Comme ci-dessus.typedef enum {
DATA_U8,
DATA_S8,
DATA_U16,
DATA_S16,
DATA_U32,
DATA_S32,
DATA_FLOAT,
}TYPE_Data;
Object165(
const TYPE_Mem typeMem,
const TYPE_Object typeObj,
const TYPE_Data typeData);
TYPE_Object GetTypeObject () const {return _typeObj;}
TYPE_Mem GetTypeMem () const {return _typeMem;}
TYPE_Data GetTypeData () const {return _typeData;}
u32* GetMemU32 () { return _mem; }
void SetMemU32 (u32* mem) { _mem = mem; }
// impossible de dériver cette méthode à cause du type de retour !
//virtual u32* GetMem () { return _mem; }
private :
TYPE_Mem _typeMem;
L'utilisation de _ comme préfixe peut poser de problèmes. Mieux
vaut comme suffixe (AMHA peu esthétique), ou un préfixe du genre
m_ ou my. Ou trouver de bons noms, pour que le préfixe ne soit
pas nécessaire.TYPE_Object _typeObj;
TYPE_Data _typeData;
u32 *_mem;
};
Plusieurs points :
-- Si j'ai bien compris, _mem pourrait pointer en fait à
n'importe quel type. Alors, c'est un void* qu'il faut.
-- Tu dérives de cette classe. Avec la possibilité éventuelle
de vouloir supprimer un objet alloué dynamiquement par un
pointeur à la base. Alors, un destructeur virtuel s'impose.
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
Dans Object j'ai donc un pointeur sur la zone mémoire (u32
*_mem) mais ce pointeur est en 32 bits.
Le pointeur est en 32 bits, ou il pointe à un bloc de mémoire de
32 bits.Or les calculs peuvent être en 8 bits par exemple.
Je cherche donc une solution pour retrouver soit un pointeur
sur la classe de base,
Si tu as un pointeur à la classe dérivée, il se convertit
implicitement en pointeur à la classe de base.soit un pointeur sur la zone mémoire de
base dans le bon type (<Type*> _mem).
N'importe quel T* peut se convertir en void*, et vice versa. La
convertion vers void* est même implicite ; pour faire celle de
void*, on se sert de static_cast. En principe, la conversion de
void* vers %* n'est valide que ce le void* result d'une
conversion du T*, mais dans la pratique, dans la mesure où il
s'agit des types fondamentaux, si le mémoire est correctement
alignée, ça doit aller.
N'empèche que je ne vois pas le problème que tu essaies à
résoudre.Par exemple, je crée 3 Objects :
const u32 szX = 20, szY = 12;
Object *obA = new ImageUser<u8>(szX, szY);
Object *obB = new ImageUser<u16>(szX, szY);
Object *obR = new ImageUser<s32>(szX, szY);
J'ai aussi créé une classe MathUser dans laquelle j'ai cette fonction :
void
MathUser::Add2Image(Object &obR, Object &obA, Object &obB)
{
// test de compatibilité entre obR, obA, obB : taille ....
// Si OK => calcul
for(u32 i=0; i<obR.GetSize()
{
// c'est là mon problème, je dois retrouver le type de base
// ce code ne peut pas etre compilé évidemment
obR.GetMem()[i] = obA.GetMem()[i] + obB.GetMem[i];
}
}
Le problème ici, c'est que Add2Image doit en fait être
polymorphique. (Ou un template, si on veut le typage statique.
C'est un cas où les templates sont bien plus simples à utiliser
que l'héritage.)
Enfin, je verrais quelque chose comme :
template< typename T >
class TypedObject : public Object ...
// Classe « typée » intermédiaire entre Object et
// UserObject ou DSPObject...
template< typename T >
void
TypedObject< T >::addToMe( Object const& other )
{
TypedObject const& rhs
= dynamic_cast< TypedObject const& >( other ) ;
if ( size() != other.size() ) {
throw bad_size() ;
}
for ( int i = 0 ; i < size() ; ++ i ) {
mem()[ i ] += rhs.mem()[ i ] ;
}
}
où addToMe est une fonction virtuelle pûre dans Object. (J'ai
un peu l'impression que l'utilisation même d'une classe de base
non-typée n'est pas une bonne idée, mais je ne connais pas assez
de l'application pour en être sûr.)J'ai mis en place un système d'ID (TYPE_Mem, TYPE_Object,
TYPE_Data) qui me permet de savoir à quel type d'objet j'ai
affaire ; c'est comme un typeid()
Et pourquoi pas se servir de typeid() ? Ou dynamic_cast ?Comment est-ce que je peux faire pour récupérer soit les
pointeurs sur ImageUser<u8> ...,
À partir d'Object* ? Avec dynamic_cast, évidemment.soit sur les données elles-mêmes : u8* ...
Une fois que tu as un pointeur sur la classe dérivée, la reste
doit couler de source. Mais en général, c'est mieux d'éviter le
besoin d'un pointeur sur la classe dérivée, avec des fonctions
virtuelles qui conviennent.J'avais pensé à un système de MACRO pour récupérer un pointeur
du bon type (sorte de downcasting) mais ça ne marche pas car
j'en ai besoin à l'éxécution et non à la compilation !
C'est pour ça qu'on a des fonctions virtuelles.Malheureusement je ne peux pas non plus utiliser une méthode
virtuelle du genre GetMem() qui me retourne un pointeur car
j'utilise des templates et donc il me faut <Type*>GetMem()
Le problème, c'est qu'il faudrait de toute façon une
implémentation spécifique de ton algorithme pour chaque type de
pointeur. La solution, SI on a besoin d'une résolution
dynamique, c'est une fonction virtuelle.
Note bien qu'au moins d'avoir absolument besoin d'une résolution
dynamique, la solution template est nettement supérieur ici. Tu
as en fait une précondition sur les types : dans le cas
d'addition, par exemple, que les deux tableaux soient de même
type. Et les templates sont conçus précisement pour exprimer des
prédicats sur les types.Est-ce qu'il y a une solution simple qui me permet de coder ce
dont j'ai besoin sans avoir à écrire toutes les combinaisons
possibles et imaginables ?
Avec une aiguillage dynamique, ça va être difficile. Peut-être
quelque chose du genre :
class AbstractOperator
{
public:
virtual ~AbstractOperator() {}
virtual void add2Image(
Object& dest,
Object const& op1,
Object const& op2 ) const = 0 ;
// ...
} ;
template< typename Result, typename Op1, typename Op2 >
class Operator : public AbstractOperator
{
public:
virtual void add2Image(
Object& dest,
Object const& op1,
Object const& op2 ) const
{
Result& d = dynamic_cast< Result >( dest) ;
Op1 const& o1 = dynamic_cast< Op1 >( op1 ) ;
Op2 const& o2 = dynamic_cast< Op2 >( op2 ) ;
// boucle avec calcul...
}
} ;
Ensuite, tu crées une instance (statique, probablement) de
chacun des types dérivés, et un std::map< TypeTriplet,
AbstractOperator const* >. Finalement, la fonction « générique »
créer un TypeTriplet à partir des types dynamiques de ses
paramètres, et cherche la classe opératrice voulue dans le map.(il y a plus de 400 combinaisons rien que pour une addition et
je dois implémenter une bonne cinquantaine d'opérandes !)
Un mélange de l'héritage, des templates, et une simulation de
l'aiguillage multiple pourraient le faire. N'empèche que
j'essaierais de rédéfinir le problème pour qu'il ne soit pas
nécessaire.
--
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
sej wrote:
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
Il y a une classe qui gère les allocations et désallocations mémoir es.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
Les copies sont interdites et l'affectation est faite par un
memcopy de la mémoire après test de compatibilité.
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des objects DSP.
En fait les objets User sont stockée en RAM du PC et les DSP dans du
hard ! Mais je ne peux pas faire de GetMem virtuel à cause du type de
retour.
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
C'est normal que les classes ne font pas de calcul car il y a trop de
cas à gérer directement : il y a 3 types d'Objects (User, PC et DSP), 7
types de donnée (u8, s8, u16, s16, u32, s32 et float), environ 5 Objects
différents (Image, scalaire, vecteur ...)
Donc pour écrire toutes les additions entre les 3 types d'objets ça en
devient impossible.
Donc la solution était de faire une classe math qui s'en charge en
'bricolant' sur le type des données.
Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
sej wrote:
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
Il y a une classe qui gère les allocations et désallocations mémoir es.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
Les copies sont interdites et l'affectation est faite par un
memcopy de la mémoire après test de compatibilité.
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des objects DSP.
En fait les objets User sont stockée en RAM du PC et les DSP dans du
hard ! Mais je ne peux pas faire de GetMem virtuel à cause du type de
retour.
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
C'est normal que les classes ne font pas de calcul car il y a trop de
cas à gérer directement : il y a 3 types d'Objects (User, PC et DSP), 7
types de donnée (u8, s8, u16, s16, u32, s32 et float), environ 5 Objects
différents (Image, scalaire, vecteur ...)
Donc pour écrire toutes les additions entre les 3 types d'objets ça en
devient impossible.
Donc la solution était de faire une classe math qui s'en charge en
'bricolant' sur le type des données.
Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
sej wrote:
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
Il y a une classe qui gère les allocations et désallocations mémoir es.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
Les copies sont interdites et l'affectation est faite par un
memcopy de la mémoire après test de compatibilité.
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des objects DSP.
En fait les objets User sont stockée en RAM du PC et les DSP dans du
hard ! Mais je ne peux pas faire de GetMem virtuel à cause du type de
retour.
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
C'est normal que les classes ne font pas de calcul car il y a trop de
cas à gérer directement : il y a 3 types d'Objects (User, PC et DSP), 7
types de donnée (u8, s8, u16, s16, u32, s32 et float), environ 5 Objects
différents (Image, scalaire, vecteur ...)
Donc pour écrire toutes les additions entre les 3 types d'objets ça en
devient impossible.
Donc la solution était de faire une classe math qui s'en charge en
'bricolant' sur le type des données.
Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
sej wrote:sej wrote:
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
Il y a une classe qui gère les allocations et désallocations mémoires.
Est-ce que ça ne serait pas mieux qu'elles soient gérer par la
classe ici ? C-à-d celle qui l'utilise, et qui sait quand elle
n'en a plus besoin.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
Les copies sont interdites et l'affectation est faite par un
memcopy de la mémoire après test de compatibilité.
C'est un peu bizarre que l'affectation soit supportée, et non la
copie. L'inverse est fréquente, mais c'est la première fois que
je vois ce cas-ci.
Je suis aussi un peu scéptique en ce qui concerne l'utilité de
l'affectation d'un objet polymorphique, étant donné qu'on ne
peut pas changer le type dynamiquement. En général, quand il le
faut (ce qui est rarement), il faut l'idiome lettre/enveloppe,
pour donner une sémantique de valeur à l'objet abstrait.
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des objects DSP.
En fait les objets User sont stockée en RAM du PC et les DSP dans du
hard ! Mais je ne peux pas faire de GetMem virtuel à cause du type de
retour.
Un getMem qui renvoie un void* ?
En fait, je me démande 1) dans quelle mesure il faut introduire
les types dans l'hièrarchie, et 2) s'il le faut, est-ce qu'une
solution mixin ne serait pas la meilleur solution.
En ce qui concerne le premier, je vois bien l'intérêt d'une
classe abstraite avec deux classes dérivées pour les sources
différentes. C'est l'intérêt d'une hièrarchie pour les types qui
me pose la question. En général, quand on utilise une classe de
base, c'est avec l'idée qu'on peut substituer n'importe quel
type dérivé à peu près partout. Or, tel que c'est partie chez
toi, il faut bien que tous les types dérivés (en ce qui concerne
le type de données) soient identiques ; tu ne peux pas
additionner les images 8 bits et les images 32 bits. Ou est-ce
que je me trompe là-dessus. En somme, il ne m'est pas trop clair
quel rôle le polymorphisme joue par rapport aux types.
Si, en revanche, il faut bien un polymorphisme sur les types
d'image, et non seulement sur les sources, je verrais bien une
solution à base de mixin. Grosso modo, tu dévinis une classe de
base abstract (probablement Image, mais surtout pas Object, qui
ne veut rien dire). Ensuite, tu implémentes les aspects qui
dépend du type dans une classe templatée (probablement) qui
dérive d'Image. Cette classe est elle aussi abstraite, parce
qu'elle n'implémente que les aspects qui dépend du type, et
laisse virtuelle pûre les fonctions qui dépend de la source.
Ensuite, tu définis deux classes dérivées d'Image qui implémente
chacune une des politiques de la source. Finalement, pour faire
des classes concrètes finale, c'est encore une classe template à
deux paramètres, à peu près :
template< typename DataType, typename SourceImpl >
class ConcreteImage : public ImageData< DataType >
, public SourceImpl
{
// Dans certains cas, on n'a même besoin de
// rien ici. Mais pas toujours.
} ;
(Du point de vue conceptuelle, on pourrait arguer que l'héritage
ici doit être « public virtual Image, private ImageData, private
SourceImpl », du fait que l'héritage de ImageData et de
SourceImpl, c'est un héritage d'implémentation, et non
d'interface, et que l'interface dont on veut hériter, c'est bien
celle de Image. Mais j'avoue que je ne suis pas aussi puriste.)template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
C'est normal que les classes ne font pas de calcul car il y a trop de
cas à gérer directement : il y a 3 types d'Objects (User, PC et DSP), 7
types de donnée (u8, s8, u16, s16, u32, s32 et float), environ 5 Objects
différents (Image, scalaire, vecteur ...)
Donc pour écrire toutes les additions entre les 3 types d'objets ça en
devient impossible.
Donc la solution était de faire une classe math qui s'en charge en
'bricolant' sur le type des données.
C'est donc bien de l'aiguillage multiple qu'il te faut.
Malheureusement (dans ton cas, au moins), le langage ne le
supporte pas directement. Ou bien, on utilise le modèle
visiteur, et il faut quand même que chaque classe en connaisse
toutes les autres, ou bien, il faut une solution à base de map,
comme j'ai présenté. Dans l'ensemble, la solution visiteur est
plus simple à mettre en oeuvre, et plus robuste, mais impose une
rétouche à toutes les classes dans l'hièrarchie chaque fois
qu'on ajoute une nouvelle classe. Elle a aussi l'avantage qu'on
peut ne prendre en compte qu'une classe intermédiaire dans
l'hièrarchie, utiliser la même fonction pour toutes les
combinaisons u32 + u8, par exemple, plutôt que d'avoir aum
minimum une entrée pour chaque combinaison de User-u32, PC-u32,
DSP-u32 et User-u8, PC-u8, DSP-u8. (C-à-d une « entrée », plutôt
que neuf.)Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Voilà la réponse à ma première question : est-ce qu'il fallait
un polymorphisme sur les types, ou non ?
[...]Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
C'est sûr que si on n'a pas besoin d'une généricité dynamique
sur le type de données, le problème est beaucoup plus facile.
--
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
sej wrote:
sej wrote:
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
Il y a une classe qui gère les allocations et désallocations mémoires.
Est-ce que ça ne serait pas mieux qu'elles soient gérer par la
classe ici ? C-à-d celle qui l'utilise, et qui sait quand elle
n'en a plus besoin.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
Les copies sont interdites et l'affectation est faite par un
memcopy de la mémoire après test de compatibilité.
C'est un peu bizarre que l'affectation soit supportée, et non la
copie. L'inverse est fréquente, mais c'est la première fois que
je vois ce cas-ci.
Je suis aussi un peu scéptique en ce qui concerne l'utilité de
l'affectation d'un objet polymorphique, étant donné qu'on ne
peut pas changer le type dynamiquement. En général, quand il le
faut (ce qui est rarement), il faut l'idiome lettre/enveloppe,
pour donner une sémantique de valeur à l'objet abstrait.
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des objects DSP.
En fait les objets User sont stockée en RAM du PC et les DSP dans du
hard ! Mais je ne peux pas faire de GetMem virtuel à cause du type de
retour.
Un getMem qui renvoie un void* ?
En fait, je me démande 1) dans quelle mesure il faut introduire
les types dans l'hièrarchie, et 2) s'il le faut, est-ce qu'une
solution mixin ne serait pas la meilleur solution.
En ce qui concerne le premier, je vois bien l'intérêt d'une
classe abstraite avec deux classes dérivées pour les sources
différentes. C'est l'intérêt d'une hièrarchie pour les types qui
me pose la question. En général, quand on utilise une classe de
base, c'est avec l'idée qu'on peut substituer n'importe quel
type dérivé à peu près partout. Or, tel que c'est partie chez
toi, il faut bien que tous les types dérivés (en ce qui concerne
le type de données) soient identiques ; tu ne peux pas
additionner les images 8 bits et les images 32 bits. Ou est-ce
que je me trompe là-dessus. En somme, il ne m'est pas trop clair
quel rôle le polymorphisme joue par rapport aux types.
Si, en revanche, il faut bien un polymorphisme sur les types
d'image, et non seulement sur les sources, je verrais bien une
solution à base de mixin. Grosso modo, tu dévinis une classe de
base abstract (probablement Image, mais surtout pas Object, qui
ne veut rien dire). Ensuite, tu implémentes les aspects qui
dépend du type dans une classe templatée (probablement) qui
dérive d'Image. Cette classe est elle aussi abstraite, parce
qu'elle n'implémente que les aspects qui dépend du type, et
laisse virtuelle pûre les fonctions qui dépend de la source.
Ensuite, tu définis deux classes dérivées d'Image qui implémente
chacune une des politiques de la source. Finalement, pour faire
des classes concrètes finale, c'est encore une classe template à
deux paramètres, à peu près :
template< typename DataType, typename SourceImpl >
class ConcreteImage : public ImageData< DataType >
, public SourceImpl
{
// Dans certains cas, on n'a même besoin de
// rien ici. Mais pas toujours.
} ;
(Du point de vue conceptuelle, on pourrait arguer que l'héritage
ici doit être « public virtual Image, private ImageData, private
SourceImpl », du fait que l'héritage de ImageData et de
SourceImpl, c'est un héritage d'implémentation, et non
d'interface, et que l'interface dont on veut hériter, c'est bien
celle de Image. Mais j'avoue que je ne suis pas aussi puriste.)
template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
C'est normal que les classes ne font pas de calcul car il y a trop de
cas à gérer directement : il y a 3 types d'Objects (User, PC et DSP), 7
types de donnée (u8, s8, u16, s16, u32, s32 et float), environ 5 Objects
différents (Image, scalaire, vecteur ...)
Donc pour écrire toutes les additions entre les 3 types d'objets ça en
devient impossible.
Donc la solution était de faire une classe math qui s'en charge en
'bricolant' sur le type des données.
C'est donc bien de l'aiguillage multiple qu'il te faut.
Malheureusement (dans ton cas, au moins), le langage ne le
supporte pas directement. Ou bien, on utilise le modèle
visiteur, et il faut quand même que chaque classe en connaisse
toutes les autres, ou bien, il faut une solution à base de map,
comme j'ai présenté. Dans l'ensemble, la solution visiteur est
plus simple à mettre en oeuvre, et plus robuste, mais impose une
rétouche à toutes les classes dans l'hièrarchie chaque fois
qu'on ajoute une nouvelle classe. Elle a aussi l'avantage qu'on
peut ne prendre en compte qu'une classe intermédiaire dans
l'hièrarchie, utiliser la même fonction pour toutes les
combinaisons u32 + u8, par exemple, plutôt que d'avoir aum
minimum une entrée pour chaque combinaison de User-u32, PC-u32,
DSP-u32 et User-u8, PC-u8, DSP-u8. (C-à-d une « entrée », plutôt
que neuf.)
Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Voilà la réponse à ma première question : est-ce qu'il fallait
un polymorphisme sur les types, ou non ?
[...]
Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
C'est sûr que si on n'a pas besoin d'une généricité dynamique
sur le type de données, le problème est beaucoup plus facile.
--
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
sej wrote:sej wrote:
-- Qui est responsable de la mémoire pointé par _mem ? Si tu
n'utilises pas le collecteur de Boehm, il faut bien s'en
occuper.
Il y a une classe qui gère les allocations et désallocations mémoires.
Est-ce que ça ne serait pas mieux qu'elles soient gérer par la
classe ici ? C-à-d celle qui l'utilise, et qui sait quand elle
n'en a plus besoin.
-- Et quel est la politique vis-à-vis des copies et de
l'affectation ? Est-ce qu'elles sont supportées, et si oui,
avec quelle sémantique, copie profonde, ou une sémantique de
référence ? (Typiquement, quand on a une hièrarchie
polymorphique, on interdit l'affectation, et souvent la
copie, ou au moins, on force à ce qu'elle passe par une
fonction virtuelle, du genre clone().)
Les copies sont interdites et l'affectation est faite par un
memcopy de la mémoire après test de compatibilité.
C'est un peu bizarre que l'affectation soit supportée, et non la
copie. L'inverse est fréquente, mais c'est la première fois que
je vois ce cas-ci.
Je suis aussi un peu scéptique en ce qui concerne l'utilité de
l'affectation d'un objet polymorphique, étant donné qu'on ne
peut pas changer le type dynamiquement. En général, quand il le
faut (ce qui est rarement), il faut l'idiome lettre/enveloppe,
pour donner une sémantique de valeur à l'objet abstrait.
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des objects DSP.
En fait les objets User sont stockée en RAM du PC et les DSP dans du
hard ! Mais je ne peux pas faire de GetMem virtuel à cause du type de
retour.
Un getMem qui renvoie un void* ?
En fait, je me démande 1) dans quelle mesure il faut introduire
les types dans l'hièrarchie, et 2) s'il le faut, est-ce qu'une
solution mixin ne serait pas la meilleur solution.
En ce qui concerne le premier, je vois bien l'intérêt d'une
classe abstraite avec deux classes dérivées pour les sources
différentes. C'est l'intérêt d'une hièrarchie pour les types qui
me pose la question. En général, quand on utilise une classe de
base, c'est avec l'idée qu'on peut substituer n'importe quel
type dérivé à peu près partout. Or, tel que c'est partie chez
toi, il faut bien que tous les types dérivés (en ce qui concerne
le type de données) soient identiques ; tu ne peux pas
additionner les images 8 bits et les images 32 bits. Ou est-ce
que je me trompe là-dessus. En somme, il ne m'est pas trop clair
quel rôle le polymorphisme joue par rapport aux types.
Si, en revanche, il faut bien un polymorphisme sur les types
d'image, et non seulement sur les sources, je verrais bien une
solution à base de mixin. Grosso modo, tu dévinis une classe de
base abstract (probablement Image, mais surtout pas Object, qui
ne veut rien dire). Ensuite, tu implémentes les aspects qui
dépend du type dans une classe templatée (probablement) qui
dérive d'Image. Cette classe est elle aussi abstraite, parce
qu'elle n'implémente que les aspects qui dépend du type, et
laisse virtuelle pûre les fonctions qui dépend de la source.
Ensuite, tu définis deux classes dérivées d'Image qui implémente
chacune une des politiques de la source. Finalement, pour faire
des classes concrètes finale, c'est encore une classe template à
deux paramètres, à peu près :
template< typename DataType, typename SourceImpl >
class ConcreteImage : public ImageData< DataType >
, public SourceImpl
{
// Dans certains cas, on n'a même besoin de
// rien ici. Mais pas toujours.
} ;
(Du point de vue conceptuelle, on pourrait arguer que l'héritage
ici doit être « public virtual Image, private ImageData, private
SourceImpl », du fait que l'héritage de ImageData et de
SourceImpl, c'est un héritage d'implémentation, et non
d'interface, et que l'interface dont on veut hériter, c'est bien
celle de Image. Mais j'avoue que je ne suis pas aussi puriste.)template<typename Type>
class ObjectUser : public Object
{
...
private :
Type *_mem;
}
template<typename Type>
class ObjectDSP : public Object;
{
...
private :
Type *_mem;
}
template<typename Type>
class ImageUser : public ObjectUser<Type>;
template<typename Type>
class ImageDSP : public ObjectDSP<Type>;
Donc en résumé, la classe de base est Object qui est dérivée
en ObjectUser et ObjectDSP. ObjectUser est dérivée en
ImageUser et d'autre classes du même genre. idem pour
ObjectDSP
J'ai utilisé des templates car je dois utiliser les types u8,
s8, u16, s16, u32, s32 et float.
Le but est de faire des calculs mathématiques sur des images
entre autre.
C'est peut-être le but du programme, mais quel rapport aux
classes présentées ici ? Elles ne font pas de calculs.
C'est normal que les classes ne font pas de calcul car il y a trop de
cas à gérer directement : il y a 3 types d'Objects (User, PC et DSP), 7
types de donnée (u8, s8, u16, s16, u32, s32 et float), environ 5 Objects
différents (Image, scalaire, vecteur ...)
Donc pour écrire toutes les additions entre les 3 types d'objets ça en
devient impossible.
Donc la solution était de faire une classe math qui s'en charge en
'bricolant' sur le type des données.
C'est donc bien de l'aiguillage multiple qu'il te faut.
Malheureusement (dans ton cas, au moins), le langage ne le
supporte pas directement. Ou bien, on utilise le modèle
visiteur, et il faut quand même que chaque classe en connaisse
toutes les autres, ou bien, il faut une solution à base de map,
comme j'ai présenté. Dans l'ensemble, la solution visiteur est
plus simple à mettre en oeuvre, et plus robuste, mais impose une
rétouche à toutes les classes dans l'hièrarchie chaque fois
qu'on ajoute une nouvelle classe. Elle a aussi l'avantage qu'on
peut ne prendre en compte qu'une classe intermédiaire dans
l'hièrarchie, utiliser la même fonction pour toutes les
combinaisons u32 + u8, par exemple, plutôt que d'avoir aum
minimum une entrée pour chaque combinaison de User-u32, PC-u32,
DSP-u32 et User-u8, PC-u8, DSP-u8. (C-à-d une « entrée », plutôt
que neuf.)Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Voilà la réponse à ma première question : est-ce qu'il fallait
un polymorphisme sur les types, ou non ?
[...]Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
C'est sûr que si on n'a pas besoin d'une généricité dynamique
sur le type de données, le problème est beaucoup plus facile.
--
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
sej wrote:sej wrote:
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des
objects DSP. En fait les objets User sont stockée en RAM du
PC et les DSP dans du hard ! Mais je ne peux pas faire de
GetMem virtuel à cause du type de retour.
Un getMem qui renvoie un void* ?
En fait, je me démande 1) dans quelle mesure il faut
introduire les types dans l'hièrarchie, et 2) s'il le faut,
est-ce qu'une solution mixin ne serait pas la meilleur
solution.
1) Le problème est de connaître le vrai type des données afin
de travailler dessus. Sinon tu proposes le void* je suppose ?
2) La solution mixin peut etre intéressante mais elle fait
appel à de l'héritage multiple ce que je ne souhaite pas
mettre en oeuvre.
Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Voilà la réponse à ma première question : est-ce qu'il fallait
un polymorphisme sur les types, ou non ?
Je pense que oui. Mais pourquoi mettre du void* ?
Comment s'en débrouiller ensuite pour accéder aux données ?
[...]Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
C'est sûr que si on n'a pas besoin d'une généricité dynamique
sur le type de données, le problème est beaucoup plus facile.
Pour l'instant ma solution est :
Object<Type> avec un pointeur <Type>* _mem dans la classe.
Et des classes dérivée templates.
J'implémente aussi des méthodes de calculs dans la classe Math
en template sur des Object<> uniquement. Concernant les
affectations, je pense qu'il est plus simple de créer une
méthode equal dans Math et d'interdire recopie et operator=
dans Object<> et ses dérivés.
Qu'en penses tu ?
sej wrote:
sej wrote:
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des
objects DSP. En fait les objets User sont stockée en RAM du
PC et les DSP dans du hard ! Mais je ne peux pas faire de
GetMem virtuel à cause du type de retour.
Un getMem qui renvoie un void* ?
En fait, je me démande 1) dans quelle mesure il faut
introduire les types dans l'hièrarchie, et 2) s'il le faut,
est-ce qu'une solution mixin ne serait pas la meilleur
solution.
1) Le problème est de connaître le vrai type des données afin
de travailler dessus. Sinon tu proposes le void* je suppose ?
2) La solution mixin peut etre intéressante mais elle fait
appel à de l'héritage multiple ce que je ne souhaite pas
mettre en oeuvre.
Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Voilà la réponse à ma première question : est-ce qu'il fallait
un polymorphisme sur les types, ou non ?
Je pense que oui. Mais pourquoi mettre du void* ?
Comment s'en débrouiller ensuite pour accéder aux données ?
[...]
Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
C'est sûr que si on n'a pas besoin d'une généricité dynamique
sur le type de données, le problème est beaucoup plus facile.
Pour l'instant ma solution est :
Object<Type> avec un pointeur <Type>* _mem dans la classe.
Et des classes dérivée templates.
J'implémente aussi des méthodes de calculs dans la classe Math
en template sur des Object<> uniquement. Concernant les
affectations, je pense qu'il est plus simple de créer une
méthode equal dans Math et d'interdire recopie et operator=
dans Object<> et ses dérivés.
Qu'en penses tu ?
sej wrote:sej wrote:
-- En fait, quel est le but de l'héritage ici, dans la mesure
où il n'y a pas de fonctions virtuelles ? Voire même, quel
est le but de la classe ? Quel est son rôle dans
l'application ? Que sont ses responsibilités ?
Le but de l'héritage est de dissocier les objects User des
objects DSP. En fait les objets User sont stockée en RAM du
PC et les DSP dans du hard ! Mais je ne peux pas faire de
GetMem virtuel à cause du type de retour.
Un getMem qui renvoie un void* ?
En fait, je me démande 1) dans quelle mesure il faut
introduire les types dans l'hièrarchie, et 2) s'il le faut,
est-ce qu'une solution mixin ne serait pas la meilleur
solution.
1) Le problème est de connaître le vrai type des données afin
de travailler dessus. Sinon tu proposes le void* je suppose ?
2) La solution mixin peut etre intéressante mais elle fait
appel à de l'héritage multiple ce que je ne souhaite pas
mettre en oeuvre.
Après réflexion, je vais passer la classe Object en template
afin d'avoir un pointeur sur le bon type de donnée et éviter
le void* (pour _mem).
Voilà la réponse à ma première question : est-ce qu'il fallait
un polymorphisme sur les types, ou non ?
Je pense que oui. Mais pourquoi mettre du void* ?
Comment s'en débrouiller ensuite pour accéder aux données ?
[...]Pour conclure, j'enlève les noeuds et donc je peux mettre mes
Objects en template et faire une classe Math template dans
laquelle je teste avec des dynamic_cast que les Objects sont
compatibles. Du coup le modèle marche beaucoup mieux.
C'est sûr que si on n'a pas besoin d'une généricité dynamique
sur le type de données, le problème est beaucoup plus facile.
Pour l'instant ma solution est :
Object<Type> avec un pointeur <Type>* _mem dans la classe.
Et des classes dérivée templates.
J'implémente aussi des méthodes de calculs dans la classe Math
en template sur des Object<> uniquement. Concernant les
affectations, je pense qu'il est plus simple de créer une
méthode equal dans Math et d'interdire recopie et operator=
dans Object<> et ses dérivés.
Qu'en penses tu ?