OVH Cloud OVH Cloud

Un petit problème de fonction virtuelle

28 réponses
Avatar
Marc G
j'ai le code suivant :

class BaseProperty {
public :
virtual BaseProperty* type(void) { return this;}
int value(void) { return -1;}
};

// représente une propriété d'un certain type
template <typename U>
class Property : public BaseProperty {
public :
Property(U* u) : _u(u) {}
virtual Property* type(void) { return this;}
U value(void) { return *_u; }
U* _u;
};

Mon problème :
j'ai lu qu'on pouvait changer le type de retour d'une fonction virtuelle
comme je le fais pour la fonction type
(et j'ai déjà utilisé avec succès cette faculté dans d'autres programmes)

si j'écris :
int y=50;
Property<int> x(&y);
BaseProperty *xx=&x;
alors
xx->type()->value() vaut -1 et pas 50
pourquoi ?
normalement, type() retourne un pointeur Property<int>* et l'appel de
value() devrait valoir 50
le pb, c'est mon compilo ou moi :-( ?
en plus, si je regarde typeid(xx->type()).name(), j'ai BaseProperty * comme
réponse
mais si je définit la fonction type() pour qu'elle retourne des références,
typeid(xx->type()).name() vaut Property
C'est à n'y rien comprendre...
Merci de vos lumières.
Marc

10 réponses

1 2 3
Avatar
James Kanze
Jean-Marc Bourguet wrote:

Le retour covariant n'est qu'un moyen d'écrire

class BaseProperty {
public:
BaseProperty* type() { return type_(); }
protected:
virtual BaseProperty* type_() { return this; }
};

template <typename U>
class Property: public BaseProperty {
public:
Property* type() { return static_cast<Property*>(type_()); }
protected:
BaseProperty* type_() { return this; }
};

Et ne change rien à la vision statique du code.


C'est une bonne façon de l'envisager, même si je crois que
l'implémentation la plus répandue correspond plutôt à :

template< typename T >
class Property : public BaseProperty
{
public:
Property* type() { return this ; } ;
protected:
BaseProperty* type_() { return type() ; }
} ;

(Et qu'évidemment, les fonctions virtuelles ne sont pas
protected, ni même pas private, mais complètement invisibles à
l'utilisateur.)

J'imagine que la raison tient aux cas où il y a l'héritage
virtuel : la conversion dérivée vers base va toujours, tandis
que la conversion base vers dérivée exige un dynamic_cast.

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

Avatar
Jean-Marc Bourguet
"James Kanze" writes:

Jean-Marc Bourguet wrote:

Le retour covariant n'est qu'un moyen d'écrire

class BaseProperty {
public:
BaseProperty* type() { return type_(); }
protected:
virtual BaseProperty* type_() { return this; }
};

template <typename U>
class Property: public BaseProperty {
public:
Property* type() { return static_cast<Property*>(type_()); }
protected:
BaseProperty* type_() { return this; }
};

Et ne change rien à la vision statique du code.


C'est une bonne façon de l'envisager, même si je crois que
l'implémentation la plus répandue correspond plutôt à :

template< typename T >
class Property : public BaseProperty
{
public:
Property* type() { return this ; } ;
protected:
BaseProperty* type_() { return type() ; }
} ;


Ça ne fonctionne que s'il est garanti que les descendant retourne this,
auquel cas, on n'a même pas besoin des membres virtuels.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Marc G
je vais étudier ta solution plus en détail et l'implémenter dans mon cas
précis.
Si ça marche, tu me sauve la vie...
pour t'expliquer mon problème en général, je développe un interpréteur, avec
des objets et donc des propriétés associées à ces objets
exemple au hasard :
UnObjetInt x=5;
x.value; // retourne 5
x.name; // propriété valide, retourne un std::string
x.nimportequoi; // erreur , la propriété n'existe pas
et pour chaque propriété publiable, je cherche à définir des objets
"propriétés" qui représente des références vers les objets "modifiables"
voilà mon problème de façon simplifiée...
Marc
Avatar
Marc G
hélas, je n'ai pas réussi à faire marcher ton code pour obtenir un T comme
type de retour
voilà ce que j'ai recopié/légèrement modifié :

class BaseProperty {
public :
//--------------------------------------------------------------------------------------------
class ReturnValue {
public :
ReturnValue(BaseProperty* owner) : _owner(owner) {}
// opérateur de conversion en type T d'un objet ReturnValue
template<typename T>
operator T() const { return dynamic_cast< Property<T>&
(*_owner).getvalue();}
// affectation d'un type T à un objet ReturnValue

template<typename T>
void operator=(T const& rhs) { dynamic_cast< Property<T>&
(*_owner).getvalue()=rhs;}
private :

BaseProperty* _owner;
};
//--------------------------------------------------------------------------------------------
virtual ReturnValue value(void)=0;
};

template <typename T>
class Property : public BaseProperty {
public :
Property(T* t) : _t(t) {}
virtual ReturnValue value(void) { return ReturnValue(this); }
T getvalue(void) { return *_t; }
protected :
T* _t;
};

et après, j'ai pas compris ta surcharge de l'operator& de ReturnValue pour
qu'il
renvoie encore un proxy...

TypeObjet y=...;
Property<TypeObjet > x(&y);
BaseProperty *xx=&x;

et je cherche à obtenir d'une manière ou d'une autre un objet de type
TypeObjet à partir de xx
si j'écris xx->value(), j'ai un objet ReturnValue et je ne sais pas a
priori quelle est la bonne conversion...
même si (TypeObjet)xx->value() est ok, face à un objet ReturnValue, je ne le
sais pas !

Il y a t-il une solution à mon problème ?
Marc

Avatar
Jean-Marc Bourguet
"Marc G" writes:

hélas, je n'ai pas réussi à faire marcher ton code pour obtenir un T comme
type de retour
voilà ce que j'ai recopié/légèrement modifié :

class BaseProperty {
public :
//--------------------------------------------------------------------------------------------
class ReturnValue {
public :
ReturnValue(BaseProperty* owner) : _owner(owner) {}
// opérateur de conversion en type T d'un objet ReturnValue
template<typename T>
operator T() const { return dynamic_cast< Property<T>&
(*_owner).getvalue();}
// affectation d'un type T à un objet ReturnValue

template<typename T>
void operator=(T const& rhs) { dynamic_cast< Property<T>&
(*_owner).getvalue()=rhs;}
private :

BaseProperty* _owner;
};
//--------------------------------------------------------------------------------------------
virtual ReturnValue value(void)=0;
};

template <typename T>
class Property : public BaseProperty {
public :
Property(T* t) : _t(t) {}
virtual ReturnValue value(void) { return ReturnValue(this); }
T getvalue(void) { return *_t; }
protected :
T* _t;
};

et après, j'ai pas compris ta surcharge de l'operator& de ReturnValue pour
qu'il
renvoie encore un proxy...

TypeObjet y=...;
Property<TypeObjet > x(&y);
BaseProperty *xx=&x;

et je cherche à obtenir d'une manière ou d'une autre un objet de type
TypeObjet à partir de xx
si j'écris xx->value(), j'ai un objet ReturnValue et je ne sais pas a
priori quelle est la bonne conversion...
même si (TypeObjet)xx->value() est ok, face à un objet ReturnValue, je ne le
sais pas !

Il y a t-il une solution à mon problème ?


Je ne suis pas sûr de comprendre ton problème. Si tu veux une vérification
statique du type, c'est impossible, tu t'es arrangé pour que l'information
ne soit pas disponible.

Si tu veux une vérification dynamique:

TypeObjet z = xx->value();

devrait fonctionner et jeter une exception si ce n'est pas du bon type. Si
tu veux, tu peux ajouter:

template <typename T> bool is() const
{
return dynamic_cast<Property<T>*>(_owner) != NULL;
}

comme membre de ReturnValue. Tu peux alors faire
xx->value().is<TypeObjet>()

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Jean-Marc Bourguet
"Marc G" writes:

Si tu veux une vérification dynamique:

TypeObjet z = xx->value();

devrait fonctionner et jeter une exception si ce n'est pas du bon type.


oui, mais
cout<<xx->value();
par exemple ne fonctionnera opas,


C'est normal, il faut avoir une idée du type à utiliser et << est
utilisable avec un tas de types.

même si << est redéfini pour TypeObjet
il faut écrire
cout<<(TypeObjet)xx->value();

et en plus, je souhaiterais éventuellement modifier la donnée
pointée/adressée (comme les getters/setters)


Tu changes dans Property
T getvalue(void) { return *_t; }
en
T& getvalue(void) { return *_t; }

et tu devrais pouvoir faire

xx->value() = une expression de type TypeObject;

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
dieu.tout.puissant
Marc G wrote:
je vais étudier ta solution plus en détail et l'implémenter dans mo n cas
précis.
Si ça marche, tu me sauve la vie...
pour t'expliquer mon problème en général, je développe un interpr éteur, avec
des objets et donc des propriétés associées à ces objets
exemple au hasard :
UnObjetInt x=5;
x.value; // retourne 5
x.name; // propriété valide, retourne un std::string
x.nimportequoi; // erreur , la propriété n'existe pas
et pour chaque propriété publiable, je cherche à définir des obje ts
"propriétés" qui représente des références vers les objets "mod ifiables"
voilà mon problème de façon simplifiée...
Marc


Reformulation (tu me dis si je me trompe) :
Donc, ton objet UnObjetInt possède 2 propriétés "value" et "name" et
tu voudrais encapsuler ces propriétés pour qu'elles soient
utilisables chacune à travers un objet (style "Property"). Tu aurais
donc 2 objets:
Property<int> value;
Property<std::string> name;

et tu voudrais accèder de manière générique à ces propriétés.

Maintenant il me faudrait un exemple d'utilisation de ces objets
propriétés. Par exemple, tu parles de xx->type()->value(), mais
comment ou à quel endroit tu utilises cette expression ?

Avatar
Marc G
Mon problème d'une manière très générale.
Je développe un interpréteur et dans mon langage, je suis susceptible de
manipuler des objets de différents type (peu importe le type) et avec des
propriétés (en lecture/écriture ou les 2)

je peux avoir par exemple un truc du genre

Type1 x;
puis
int y=x.size;

mon problème est de savoir rapidement et simplement si la propriété size
existe pour Type1 et d'obtenir "facilement" une référence (constante le cas
échéant) vers la donnée membre correspondante (par exemple).

D'où mon idée de développer une classe Property, avec l'idée de l'utiliser
comme suit

// ne dispose que de données membres static
template <typename T>
class CObjectWithProperty {

template <typename U>
static void add_property(Property<U> const&); // "j'enregistre"
des propriétés

// etc...
};
je ne développe pas ici la classe CObjectWithProperty mais l'idée c'est
qu'elle gère une liste de propriétés accessibles pour un objet, avec leur
nom associé (CObjectWithProperty<Type1> classe associée aux objets Type1)
Seulement on ne construit malheureusement pas une liste avec des objets
hétérogènes.
Ma liste contient donc des objets de la seule classe de base de Property et
mon problème c'est d'obtenir un objet un objet T à partir de la classe
BaseProperty , classe de base de Property<T>
J'ai conscience que j'esplique pas trop bien, hélas...
Pour mieux comprendre, regarde le code 2 niveaux + haut.

En fait, le type ReturnValue convient comme lvalue (donc setter) en
redéfinissant l'opérateur = dans ReturnValue
Mon problème, c'est plutôt comme rvalue
ex :
Type1 x;
Type1 y;
int m= x.size*y.size // je fais quoi là :-(
-> je suis obligé de redéfinir tous les opérateurs
mais comme tu sais on peut définir ses propres opérateurs pour un type
d'objet et d'une manière générique, je suis incapable de le savoir....
J'espère que tu m'as compris !
Marc A +
Avatar
dieu.tout.puissant
Marc G wrote:
Mon problème d'une manière très générale.
Je développe un interpréteur et dans mon langage, je suis susceptible de
manipuler des objets de différents type (peu importe le type) et avec d es
propriétés (en lecture/écriture ou les 2)

je peux avoir par exemple un truc du genre

Type1 x;
puis
int y=x.size;

mon problème est de savoir rapidement et simplement si la propriété size
existe pour Type1 et d'obtenir "facilement" une référence (constante le cas
échéant) vers la donnée membre correspondante (par exemple).

D'où mon idée de développer une classe Property, avec l'idée de l 'utiliser
comme suit

// ne dispose que de données membres static
template <typename T>
class CObjectWithProperty {

template <typename U>
static void add_property(Property<U> const&); // "j'enregistre"
des propriétés

// etc...
};
je ne développe pas ici la classe CObjectWithProperty mais l'idée c'e st
qu'elle gère une liste de propriétés accessibles pour un objet, ave c leur
nom associé (CObjectWithProperty<Type1> classe associée aux objets Ty pe1)
Seulement on ne construit malheureusement pas une liste avec des objets
hétérogènes.
Ma liste contient donc des objets de la seule classe de base de Property et
mon problème c'est d'obtenir un objet un objet T à partir de la classe
BaseProperty , classe de base de Property<T>
J'ai conscience que j'esplique pas trop bien, hélas...
Pour mieux comprendre, regarde le code 2 niveaux + haut.

En fait, le type ReturnValue convient comme lvalue (donc setter) en
redéfinissant l'opérateur = dans ReturnValue
Mon problème, c'est plutôt comme rvalue
ex :
Type1 x;
Type1 y;
int m= x.size*y.size // je fais quoi là :-(
-> je suis obligé de redéfinir tous les opérateurs
mais comme tu sais on peut définir ses propres opérateurs pour un type
d'objet et d'une manière générique, je suis incapable de le savoir. ...
J'espère que tu m'as compris !
Marc A +


Oui, la classe ReturnValue n'adresse pas le vrai problème que tu
poses.

En rendant générique le traitement des propriétés, tu perds le type
statique de ces propriétés, et le compilateur n'a pas de moyen direct
de retrouver ce type.
Tu peux, à coup de dynamic_cast (entre autres solutions) , amener une
propriété BaseProperty* vers une partie du code qui traitera
spécifiquement la propriété comme si le type statique de cette
dernière était égal à son type dynamique "sous-jacent". Mais,
évidemment, c'est une solution fastidieuse et peu ouverte au
changement.
D'où la question :

Est-ce que le nom et le nombre des propriétés sont connus et plutôt
fixes ou bien est-ce qu'ils sont inconnus ou amené à fréquemment
changer lors de la mise à jour du code, sa maintenance, etc... ?

Avatar
Marc G
Est-ce que le nom et le nombre des propriétés sont connus et plutôt
fixes ou bien est-ce qu'ils sont inconnus ou amené à fréquemment
changer lors de la mise à jour du code, sa maintenance, etc... ?


Les propriétés sont connues et fixes mais je cherchais un moyen de rendre le
code générique...
en enregistrant automatiquement les propriétés selon un modèle fixe
A + ... et bonne nuit

1 2 3