[C++]appel de fonction pure

appel de fonction pure [C++] - C++ - Programmation

Marsh Posté le 02-12-2005 à 18:09:21    

Code :
  1. #include <iostream>
  2. #include <conio.h>
  3. using namespace std;
  4. class BaseValue
  5. {
  6. public:
  7. virtual ~BaseValue(){}
  8. virtual const type_info & GetType() const = 0;
  9. };
  10. template<class ValueType>
  11. class TypeValue : public BaseValue
  12. {
  13. public:
  14. TypeValue(const ValueType & value)
  15.  : Value(value){ }
  16. virtual const type_info & GetType() const {
  17.  return typeid(Value);
  18. }
  19. ValueType Value;
  20. };
  21. class RefAny
  22. {
  23. public: // structors
  24. RefAny(){ }
  25. template<class ValueType>
  26.  RefAny(const ValueType & value)
  27.  : content(&TypeValue<ValueType>(value)) { }
  28. template<typename ValueType>
  29.  void GetValue(ValueType * value){
  30.   *value = static_cast<TypeValue<ValueType>*>(content)->Value;
  31.  }
  32. template<typename ValueType>
  33.  void GetValueType(ValueType * value){
  34.   if (content->GetType() == typeid(ValueType)) // content->type() plantage appel de fonction pure
  35.    *value = static_cast<TypeValue<ValueType>*>(content)->Value;
  36. //   else
  37. //    wxASSERT(false);
  38.  }
  39. BaseValue* content;
  40. };
  41. int main()
  42. {
  43. int  Entier;
  44. double Reel;
  45. RefAny hTemp(3.14);
  46. hTemp.GetValue(&Reel);
  47. hTemp = 345;
  48. hTemp.GetValue(&Entier);
  49. hTemp = 784;
  50. hTemp.GetValueType(&Entier); // --> plantage appel de fonction pure
  51. hTemp.GetValueType(&Reel); // --> plantage appel de fonction pure
  52. std::cout << "Appuyer sur une touche..." << endl;
  53. _getch();
  54. }


 
content->GetType()  devrait faire un appel à la méthode GetType() de TypeValue mais produit soit un plantage soit un appel de fonction pure de BaseValue
Je pense que le problème doit venir de l'utilisation de template, mais j'aimerais bien comprendre...


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 02-12-2005 à 18:09:21   

Reply

Marsh Posté le 02-12-2005 à 18:52:08    

(&TypeValue<ValueType>(value))    
 
adresse d'une variable locale à durée de vie courte ?

Reply

Marsh Posté le 02-12-2005 à 19:21:57    

: content(&TypeValue<ValueType>(value))    {    } initialise le pointeur BaseValue* content; avec l'adresse de la variable passée au constructeur par exemple "&Reel",
cette variable est détruite à la fin du main() il n'y a donc pas ce problème surtout que les appels hTemp.GetValue(&Entier); fonctionnent bien.


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 02-12-2005 à 19:57:54    

shaman3 a écrit :

: content(&TypeValue<ValueType>(value))    {    } initialise le pointeur BaseValue* content; avec l'adresse de la variable passée au constructeur par exemple "&Reel",


 
non, jesus a (presque) raison. tu initialises BaseValue* avec l'adresse d'un temporaire ... Après, t'as une splendide erreur. je suis étonné que ton compilateur ne t'ai pas averti.


Message édité par ++fab le 02-12-2005 à 20:02:05
Reply

Marsh Posté le 02-12-2005 à 21:09:40    

Merci pour votre aide, effectivement j'ai zappé la création de cette variable temporaire, mais ce qui est étonnant dans cette erreur c'est que visual studio ne la pas signalée et que le plantage se produise après les appels hTemp.GetValue(&Reel);et de hTemp.GetValue(&Entier); qui fonctionne !!! sur une variable qui aurait du déjà être détruite à ce moment... Le fait que cette variable n'est pas détruite immédiatement génère des erreurs à des endroits différents (bonjour la prise de tête).
jesus_christ : Je te donne le droit de me crucifier sur la place publique :)


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 02-12-2005 à 21:41:12    

shaman3 a écrit :

Merci pour votre aide, effectivement j'ai zappé la création de cette variable temporaire, mais ce qui est étonnant dans cette erreur c'est que visual studio ne la pas signalée et que le plantage se produise après les appels hTemp.GetValue(&Reel);et de hTemp.GetValue(&Entier); qui fonctionne !!!


 
BaseValue* content pointe on ne sait ou, en tout cas, plus vers une instance de BaseValue ou une instance d'une classe dérivée de BaseValue.
D'ou l'appel irréel à une fonction virtuelle pure.
 
D'autre part, tes warnings ne sont pas bien positionnés, c'est pas possible que ton compilo ne puissent rien dire !


Message édité par ++fab le 02-12-2005 à 21:43:32
Reply

Marsh Posté le 02-12-2005 à 22:19:08    

"warnings"  tu veux dire les points d'arrêt non ? En mode debug avec visual studio 2003 puis en mode pas à pas (touche F10 et F11) il exécute sans problèmes les deux appels hTemp.GetValue(&Reel);et hTemp.GetValue(&Entier); et le contenu des variables est correct, bref je suis aussi étonné que toi de ce comportement. Que dit la norme la dessus ? comment se comporte les d'autres compilateurs comme Gcc sur ce problème ?


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 02-12-2005 à 22:19:55    

Juste pour info le programme pris sans modif ne compile pas sous devC++. Avec un message indiquant ce que jesus mentionnait.
 
Stef

Reply

Marsh Posté le 02-12-2005 à 22:28:58    

si warning, prendre l'adresse d'un temporaire pour l'affecter à un pointeur, c'est tres louche.
gcc réagit de la meme maniere, mais emet des warnings explicite.

Reply

Marsh Posté le 02-12-2005 à 22:45:52    

En fait si, mais il faut passer en Niveau 4 (/W4) pour avoir ça :
e:\programmation\SOURCE_dotnet\console\Array_multi_type\Array_multi_type.h(246) : warning C4238: extension non standard utilisée : classe rvalue utilisée comme lvalue
        main.cpp(552) : voir la référence à l'instanciation du modèle de fonction 'RefAny::RefAny<double>(const ValueType & )' en cours de compilation
        with
        [
            ValueType=double
        ]
e:\programmation\SOURCE_dotnet\console\Array_multi_type\Array_multi_type.h(246) : warning C4238: extension non standard utilisée : classe rvalue utilisée comme lvalue
        main.cpp(557) : voir la référence à l'instanciation du modèle de fonction 'RefAny::RefAny<int>(const ValueType & )' en cours de compilation
        with
        [
            ValueType=int
        ]
pour le plantage différé quelqu'un m'indique que c'est normal :
>il s'agit de ce qu'on appelle un "comportement
>indéfini" -- la norme ne prévoit pas ce qui va se passer, et
>effectivement on a un plantage à un endroit bizarre, éventuellement
>aléatoire."
Donc c'est moi qui à tout faux(comme dab), et visual fonctionne parfaitement.


Message édité par shaman3 le 02-12-2005 à 22:59:51

---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 02-12-2005 à 22:45:52   

Reply

Marsh Posté le 02-12-2005 à 23:28:45    

je viens de voir le thread. Oui, c'est UB, mais je ne sais pas si le warning que donne VS est bon.  
3.10.6 dit que ton temporaire est une rvalue. Jusque la, c'est bon, sauf erreur. Apres du demandes l'adresse de la rvalue, il me semblait que c'était illégal mais je n'en suis plus du tout sur puisque je ne retrouve pas la référence :/

Reply

Marsh Posté le 03-12-2005 à 08:42:41    

virtual const type_info & GetType() const = 0;
 
moi j'adhère pas trop à ça parce que ça a une utilité très limité (voire aucune). Par contre, l'idiome du constructeur virtuel et une fonction membre clone(), ça c'est utile.

Reply

Marsh Posté le 03-12-2005 à 12:21:02    

virtual const type_info & GetType() const = 0;  
me permet de faire une comparaison de type depuis un pointeur BaseValue* ce qui rend le static_cast suivant sûr.
Effectivement le constructeur virtuel (ou plutôt une fonction membre virtual clone() qui appelle le constructeur de l'objet) peut être utile à la création d'un objet de même type et de contenu depuis un type de base.


Message édité par shaman3 le 03-12-2005 à 12:29:47

---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 03-12-2005 à 16:42:27    

Citation :

comparaison de type depuis un pointeur BaseValue* ce qui rend le static_cast suivant sûr.


et le dynamic_cast ?

Reply

Marsh Posté le 03-12-2005 à 19:15:40    

+1 pour dynamic_cast.
 
Ce que tu fais c'est mauvais (et potentiellement lent) et peu souple car tu brides ton système de typage.
 
et si tu veux comparer, tu fais
 
typeid(*object) == typeid(TonType)
 
et voilà.
 
 

Reply

Marsh Posté le 05-12-2005 à 01:27:46    

Après vérification  

Code :
  1. TypeValue<ValueType>* ptCastValue = dynamic_cast<TypeValue<ValueType>*>(content);
  2. if (ptCastValue)
  3. *value = ptCastValue->Value;


le dynamic_cast est plus de 2x plus lent que :

Code :
  1. if (content->GetType() == typeid(ValueType))
  2.        *value = static_cast<TypeValue<ValueType>*>(content)->Value;


En quoi je bride mon système de typage ?
pour typeid(*object) == typeid(TonType)  
BaseValue* n'a pas accès à Value, je ne vois pas comment tu veux le tester directement.
 


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 05-12-2005 à 10:09:31    

déjà c'est
 
if (T* t = dynamic_cast<T*>(x)) {
 
}
 
ensuite je ne vois pas pourquoi ça serait plus lent.
Et ça bride parce que tu fais des tests d'équivalence, tu testes : "est-ce que le type réel de truc est Machin ?" et non pas "est-ce que truc est un Machin ?"

Reply

Marsh Posté le 05-12-2005 à 11:16:30    

Même chose pour :

Code :
  1. if (TypeValue<ValueType>* ptCastValue = dynamic_cast<TypeValue<ValueType>*>(content))
  2.        *value = ptCastValue->Value;


c'est toujours 2 x plus lent(c'est exactement le même code asm une fois compilé), d'ailleurs ce n'est pas pour rien que la technique(virtual const type_info & GetType() const = 0; ) est utilisée dans la librairie boost.
"est-ce que le type réel de truc est Machin ?" et non pas "est-ce que truc est un Machin ?" oui et alors ca ne change rien au niveau utilisation, je préfère avoir de meilleurs perfs.


Message édité par shaman3 le 05-12-2005 à 11:17:59

---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 05-12-2005 à 12:33:02    

bien sur que si ça change ... mais bon, on va pas t'apprendre la POO

Reply

Marsh Posté le 05-12-2005 à 14:40:05    

Mais j'apprends des choses tous les jours en c++, pour info :  
Performance of typeid vs. dynamic_cast<>
As far as design is concerned, dynamic_cast<> should be preferred to typeid because the former enables more flexibility and extensibility. Notwithstanding that, the runtime overhead of typeid can be less expensive than dynamic_cast<>, depending on the operands. An invocation of operator typeid is a constant time operation--it takes the same length of time to retrieve the runtime type information of every polymorphic object, regardless of the object's derivational complexity. On the other hand, dynamic_cast<> is not a constant time operation. It has to traverse the derivation tree of the operand until it has located the target object in it. The worst case scenario is when the operand is a deeply derived object and the target is a non-related class type. Then, dynamic_cast<> has to traverse the entire derivation tree before it can confidently decide that requested cast cannot be done.
 
Et je pense que c'est pour cette raison que les developpeurs de boost utilisent typeid


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 05-12-2005 à 15:46:40    

Tu devrais jeter un oeil à boost::any.


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 05-12-2005 à 16:22:48    

Justement ca s'adresse à Taz alors parce qu'ils utilisent un joli :
virtual const std::type_info & type() const = 0;
puis pour faire un cast :

Code :
  1. template<typename ValueType>
  2.     ValueType * any_cast(any * operand)
  3.     {
  4.         return operand && operand->type() == typeid(ValueType)
  5.                     ? &static_cast<any::holder<ValueType> *>(operand->content)->held
  6.                     : 0;
  7.     }



---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 05-12-2005 à 16:54:31    

boost a aussi la contrainte d'être hautement portable, et certains compilos ne supportent / supportait pas le rtti peut être.
Après faut voir. Dans le cas général, avec hiérarchie de classes, dynamic_cast est préférable car il gère le polymorphisme. Dans ton cas particulier, ça se défend.
Si tu vises vraiments les performances, faut rester en typage statique (genre union à la VARIANT). Je suis pas sûr que le rtti impacte beaucoup en comparaison de l'allocation dynamique.
Et personnelement c'est plutôt l'existance même de ta classe que je trouve discutable. C'est pas trop dans l'esprit C++ ce genre de pratiques. Dans quel contexte tu t'en sers ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 05-12-2005 à 19:01:50    

HelloWorld a écrit :

boost a aussi la contrainte d'être hautement portable, et certains compilos ne supportent / supportait pas le rtti peut être.


 
typeid sans support du rtti, ça va poser problème aussi.

Reply

Marsh Posté le 05-12-2005 à 19:03:30    

C'est juste une class de test, car je voulais stoker un pointeur et éviter la recopie des gros objets comme dans boost::any.Le but est de pouvoir transmettre des paramètres "multi-type" à une fonction, mais j'ai beau chercher il n'y a pas de solution-miracle à ce problème (rapide comme un void* mais avec un typage fort). Au final je vais garder mes fonctions surchargées par type de paramètre.


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le 05-12-2005 à 19:05:13    

shaman3 a écrit :

Mais j'apprends des choses tous les jours en c++, pour info :  
Performance of typeid vs. dynamic_cast<>
As far as design is concerned, dynamic_cast<> should be preferred to typeid because the former enables more flexibility and extensibility. Notwithstanding that, the runtime overhead of typeid can be less expensive than dynamic_cast<>, depending on the operands. An invocation of operator typeid is a constant time operation--it takes the same length of time to retrieve the runtime type information of every polymorphic object, regardless of the object's derivational complexity. On the other hand, dynamic_cast<> is not a constant time operation. It has to traverse the derivation tree of the operand until it has located the target object in it. The worst case scenario is when the operand is a deeply derived object and the target is a non-related class type. Then, dynamic_cast<> has to traverse the entire derivation tree before it can confidently decide that requested cast cannot be done.
 
Et je pense que c'est pour cette raison que les developpeurs de boost utilisent typeid


 
la, on compare les performances de 2 opérations qui ont des objectifs différents ... boost::any utilise typeid parce que c'est le comportement qu'on s'attend à trouver.

Reply

Marsh Posté le 06-12-2005 à 10:33:17    

shaman3 a écrit :

C'est juste une class de test, car je voulais stoker un pointeur et éviter la recopie des gros objets comme dans boost::any.Le but est de pouvoir transmettre des paramètres "multi-type" à une fonction, mais j'ai beau chercher il n'y a pas de solution-miracle à ce problème (rapide comme un void* mais avec un typage fort). Au final je vais garder mes fonctions surchargées par type de paramètre.


Et une fonction template ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 06-12-2005 à 16:05:29    

oui afin dans mon cas, il me faut également y avoir accès via pointeur depuis la class de base abstraite.  
Je vois quelques possibilités mais l'utilisation des templates me pose des problèmes d'instanciation, de spécialisation de méthode, en plus avec les templates l'implémentation se retrouve dans la déclaration ce qui génére des définitions multiples, des dépendances, une moins bonne lisibilité du code, et la recompilation des fichiers dépendants lorsqu'il y a modification de l'implémentation, bref je commence à me demander s'il faut les utilisées dans mon cas.


---------------
L'agence www.PolyDev.com, entreprise spécialisée dans l'accompagnement et le suivi de vos projets multimédia (3d, 3d web, site web,  logiciels spécialisés).
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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