1. #1
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut Comportement erratique en héritage

    Bonjour à tous, mon problème a certainement été évoqué mais tous mes axes de recherche mènent à des recherches infructueuses...

    Le voici donc :
    J'ai développé 2 classes dont une qui est la fille de l'autre

    headers :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    class Parent {
    int val;
     
    Parent();
    virtual ~Parent();
    void start(void);
    virtual void run(void);
    }
     
    class Enfant : Parent {
    Enfant();
    virtual ~Enfant();
    void run(void);
    }
    implémentation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    Parent::Parent() { }
    Parent::~Parent() { }
    void Parent::start() {
    boost::thread(boost::bind(&Parent::run, this));
    }
    void Parent::run() { std::cout << "problème" << std::endl; }
     
    Enfant::Enfant() { }
    Enfant::~Enfant() { }
    void Enfant::run() { std::cout << "Ok" << std::endl; }
    voici mon main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    main() {
    Enfant enfant;
    enfant.start();
    }
    Vous l'avez compris je devrais avoir dans 100% des cas :
    "Ok"

    Malheureusement en réalité je n'ai ce résultat que dans 90% des cas environ. Parfois il m'affiche "Problème", ce qui signifie que la méthode run() du parent a été appelée...
    J'en déduis que l'appel a start a été effectué dans le parent et éventuellement que l'enfant n'a même pas été complètement instancié.
    Que dois-je en conclure ? Est-ce un problème conceptuel ou bien d'implémentation ? Est-ce une particularité de boost::thread que je n'aurais pas pris en compte ?

    Merci pour votre aide
    Tristan

  2. #2
    Membre averti Avatar de Nogane
    Profil pro
    Inscrit en
    juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations forums :
    Inscription : juin 2008
    Messages : 241
    Points : 323
    Points
    323

    Par défaut

    Bonjour,
    Il est clair que ce code n'est pas celui que vous utilisez vraiment(il n'est pas compilable) donc je ne peut que faire des suppositions.
    En générale ce genre de problème est lié a du "slicing". C'est ce qui arrive quand on copie un objet enfant dans un objet ancêtre. Par exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Enfant enfant;
    Parent parent = enfant; //Et la, c'est le drame
    parent.start();
    Le plus simple pour éviter a coup sur ce genre d’erreur, c'est de rendre les class de base non copiable:
    - En déclarant le constructeur par copie et l'operateur = en privé(sans les implémenter)
    - Ou en héritant de boost::noncopyable

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut

    Bonjour

    merci pour ta réponse !
    C'est vrai que je n'ai pas mis le code original mais une version un peu allégée...

    J'ai opéré les modifications proposées en faisant hériter ma classe de boost::noncopyable et j'ai toujours les mêmes comportements...

    Cdlt

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut

    Allez voici le code ça sera plus facile pour décrypter le problème !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
    /* 
     * File:   OPThread.h
     * Author: tristan
     *
     * Created on 29 juin 2011, 22:05
     */
     
    #ifndef OPTHREAD_H
    #define	OPTHREAD_H
     
    #include <boost/shared_ptr.hpp>
    #include <boost/thread.hpp>
    #include <boost/function.hpp>
    #include <boost/noncopyable.hpp>
     
    #include "opus/flow/Threadable.h"
     
    namespace opus {
        namespace flow {
     
            class OPThread : boost::noncopyable {       
            protected:            
                bool running_;
                bool shouldStop_;
                boost::shared_ptr<boost::thread> thread_;            
     
            public:                 
                OPThread();
                virtual ~OPThread();
                void start();
                void stop(void);
                bool isRunning(void);
                void join(void); 
                void timed_join(time_t millis); 
                bool shouldStop(void);
                void sleep(unsigned int millis);            
     
            protected:
                void _start(void);
                virtual void run(void);            
            };
            typedef boost::shared_ptr<OPThread> OPThreadPtr;
        }
    }
     
    #endif	/* OPTHREAD_H */
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    #include <iostream>
    #include <boost/thread.hpp>
     
    #include "OPThread.h"
    #include "opus/Logger.h"
    #include "opus/system/System.hpp"
     
    namespace opus {
        namespace flow {
     
            OPThread::OPThread() {
                this->running_ = false;
                this->shouldStop_ = false;
            }
     
            OPThread::~OPThread() {
     
            }
     
            void OPThread::start() {
                if (!(this->running_) && !(this->shouldStop_)) {
                    this->thread_ = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&OPThread::run, this)));    
                }
            }
     
            void OPThread::stop(void) {
                if ((this->running_) && (!this->shouldStop_)) {
                    this->shouldStop_ = true;
                }
            }
     
            bool OPThread::isRunning(void) {
                if(this->thread_)
                    return this->running_;
                else
                    return false;
            }
     
            void OPThread::join() {
                if(this->thread_)
                    this->thread_->join();
            }
     
            void OPThread::timed_join(time_t millis) {
                if(this->thread_)
                    this->thread_->timed_join(boost::posix_time::milliseconds(millis));
            }
     
            void OPThread::run() {
                opus::Logger logger = opus::Logger::getInstance();
                logger.error("This thread class does not have a run method !");
            }
     
            bool OPThread::shouldStop() {
                return this->shouldStop_;
            }
     
            void OPThread::sleep(unsigned int millis) {
                opus::system::sleepInterruptible(millis, boost::bind(&OPThread::shouldStop, this), 100);
            }
        }
    }
    La classe enfant (c'est un test unitaire) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    #define TEST_VAL 28
    class ThreadTest : public opus::flow::OPThread {
        //friend class opus::flow::OPThread;
        int val;
        bool inf;
    public:
        ThreadTest(bool infinite = false) { 
            val = 0; 
            inf = infinite; 
        };
        virtual ~ThreadTest() { };
        int getVal() { return val; };    
     
        void run() {        
            if(!inf) {
                for(int i = 0 ; i < TEST_VAL ; i++) {
                    val += 1;
                }
            } else {
                while(!shouldStop_) {
                    val += 1;
                    this->sleep(1000);
                }
            }
        };
    };
    typedef boost::shared_ptr<ThreadTest> ThreadTestPtr;
    Et l'appel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
     ThreadTestPtr th1(new ThreadTest(true));
            ThreadTestPtr th2(new ThreadTest(true));
            ThreadTestPtr th3(new ThreadTest(true));
            ThreadTestPtr th4(new ThreadTest(true));
     
            th1->start();
            th2->start();
            th3->start();
            th4->start();
    Le résultat :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    ERROR - 20110701105954 - This thread class does not have a run method !
    ERROR - 20110701105954 - This thread class does not have a run method !
    ERROR - 20110701105954 - This thread class does not have a run method !
    ERROR - 20110701105954 - This thread class does not have a run method !
    ERROR - 20110701105954 - This thread class does not have a run method !
    ERROR - 20110701105955 - This thread class does not have a run method !
    ERROR - 20110701105955 - This thread class does not have a run method !
    Segmentation fault: 11
     : OK
    L'exception dans le détail :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000000
    0x0000000000000000 in ?? ()
     
    #0  0x0000000000000000 in ?? ()
    #1  0x0000000100071c19 in atomic_exchange_and_add [inlined] () at :145
    #2  0x0000000100071c19 in boost::detail::sp_counted_base::weak_release () at :157
    #3  0x0000000100071c19 in boost::detail::shared_count::~shared_count (this=<value temporarily unavailable, due to optimizations>) at sp_counted_base_gcc_x86.hpp:146
    #4  0x000000010003b947 in boost::shared_ptr<boost::thread>::~shared_ptr (this=0x100500420) at shared_ptr.hpp:169
    #5  0x000000010003ae74 in opus::flow::OPThread::~OPThread (this=0x100500410) at OPThread.cpp:18
    #6  0x0000000100022884 in ThreadTest::~ThreadTest (this=0x100500410) at newtestclass8.h:26
    #7  0x00000001000044ba in boost::checked_delete<ThreadTest> (x=0x100500410) at checked_delete.hpp:34
    #8  0x0000000100004466 in boost::detail::sp_counted_impl_p<ThreadTest>::dispose (this=0x100500440) at sp_counted_impl.hpp:78
    #9  0x0000000100071c19 in atomic_exchange_and_add [inlined] () at :145
    #10 0x0000000100071c19 in boost::detail::sp_counted_base::weak_release () at :157
    #11 0x0000000100071c19 in boost::detail::shared_count::~shared_count (this=<value temporarily unavailable, due to optimizations>) at sp_counted_base_gcc_x86.hpp:146
    #12 0x000000010000323d in boost::shared_ptr<ThreadTest>::~shared_ptr (this=0x7fff5fbff270) at shared_ptr.hpp:169
    #13 0x0000000100002258 in newtestclass8::testMultiple (this=0x100414aa0) at newtestclass8.cpp:41
    #14 0x0000000100007644 in CppUnit::TestCaller<newtestclass8>::runTest (this=0x100414b20) at TestCaller.h:166
    #15 0x0000000100066234 in CppUnit::TestCaseMethodFunctor::operator() (this=0x100414b20) at TestCase.cpp:32
    #16 0x000000010005ffb4 in CppUnit::DefaultProtector::protect (this=0x100414530, functor=@0x100500000, context=@0x7fff5fbff580) at DefaultProtector.cpp:15
    #17 0x0000000100064841 in CppUnit::ProtectorChain::ProtectFunctor::operator() (this=0x100414b20) at ProtectorChain.cpp:20
    #18 0x00000001000644d0 in CppUnit::ProtectorChain::protect (this=0x100414290, functor=@0x7fff5fbff600, context=@0x7fff5fbff570) at ProtectorChain.cpp:77
    #19 0x000000010006b0e0 in CppUnit::TestResult::protect (this=0x7fff5fbff920, functor=@0x100500000, test=0x7fff5fbff580, shortDescription=@0x100500000) at TestResult.cpp:178
    #20 0x0000000100065e45 in CppUnit::TestCase::run (this=0x100414b20, result=0x7fff5fbff920) at TestCase.cpp:92
    #21 0x0000000100066375 in CppUnit::TestComposite::doRunChildTests (this=0x100413e80, controller=0x7fff5fbff920) at TestComposite.cpp:64
    #22 0x00000001000662a1 in CppUnit::TestComposite::run (this=0x100413e80, result=0x7fff5fbff920) at TestComposite.cpp:23
    #23 0x0000000100066375 in CppUnit::TestComposite::doRunChildTests (this=0x100414a60, controller=0x7fff5fbff920) at TestComposite.cpp:64
    #24 0x00000001000662a1 in CppUnit::TestComposite::run (this=0x100414a60, result=0x7fff5fbff920) at TestComposite.cpp:23
    #25 0x000000010006b070 in CppUnit::TestResult::runTest (this=0x7fff5fbff920, test=0x100414a00) at TestResult.cpp:145
    #26 0x000000010006d6c6 in CppUnit::TestRunner::run (this=0x100414b20, controller=@0x100414b20, testPath=@0x100414b20) at TestRunner.cpp:96
    #27 0x00000001000230ab in main () at newtestrunner13.cpp:30

  5. #5
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut

    et si on retire l'héritage de boost::uncopyable on a moins de "This thread class does not have a run method !" et autant d'exceptions

  6. #6
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    juin 2008
    Messages
    7 636
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2008
    Messages : 7 636
    Points : 13 367
    Points
    13 367

    Par défaut

    Salut,

    NVI + virtuelle pure devrait embêter ton compilateur si tu as un pb :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class OPThread : boost::noncopyable
    {
    // [...]
       void run(void) // la fonction n'est plus virtuelle
       {do_run();}
    private:
       virtual void do_run()=0;
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class ThreadTest
    {
    //[...]
    private:
       virtual void do_run()
       {
       }
    };
    Môsieur vient du Java ou de .Net ?
    Pas besoin de this-> en C++ (sauf cas très très particulier)

    running_ et should_stop_, ça pue à 100 mètres.

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut

    Quel idiot ! Le nvi... Je l'ai utilisé dans une autre classe déjà pour les mêmes raisons ... Arf . Merci. Cela dit c'est vrai que je n'ai toujours pas compris exactement ce qui se passait dans l'héritage pour ça ne fonctionne pas correctement.

    Par ailleurs qu'est ce qui te chagrine avec les running_ et shouldStop_ ?

    Merci

  8. #8
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut

    Allons bon ! Je viens d'implémenter ta solution (passer le run en méthode normale et ajouter un do_run private virtuel) et je n'ai pas d'amélioration, le programme continue à me dire que la méthode que j'appelle (à savoir run()) est virtuelle pure.

    Dans ma première question je me demandais si boost::bind faisait bien son boulot vis-à-vis de this. Est-ce qu'il va considérer l'instance enfant ou bien l'instance parent ?

  9. #9
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    août 2004
    Messages
    5 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 5 372
    Points : 15 051
    Points
    15 051

    Par défaut

    J'ai juste parcouru le code, et j'y vois quelques soucis, mais ne sais pas si c'est lié (peut-être qu'une version mono-fichier de l'ensemble de code, directement compilable, permettrait de mieux comprendre ce qui se passe) :

    Tes objets sont gérés par shared_ptr... Sauf dans bind, où tu passes un pointeur nu, qui ne va donc pas incrémenter le compteur de référence.
    Par ailleurs qu'est ce qui te chagrine avec les running_ et shouldStop_
    Je ne sais pas ce qui chagrine 3DArchi, mais ce qui me chagrine moi, c'est que ces variables sont clairement destinées à être accédées depuis plusieurs threads, mais que l'on y accède sans aucun mécanisme de synchronisation.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  10. #10
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut

    ah oui excellente remarque, je devrais probablement passer un shared_from_this n'est-ce pas ?

    Pense-tu que cela puisse expliquer pourquoi il ne trouve pas la méthode run surchargée ? Je vais tester ça immédiatement.

    Concernant les variables, je ne pense pas que cela pose problème car cette classe est destinée à encapsuler un unique thread. Elle est instanciée 1 fois pour 1 exécution, il ne peut y avoir d'accès concurrentiel aux variables.

    Merci !

  11. #11
    Membre habitué
    Profil pro
    Inscrit en
    décembre 2002
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2002
    Messages : 230
    Points : 130
    Points
    130

    Par défaut

    Bon je pense que ce que je voulais faire était un peu léger conceptuellement. J'ai donc évidemment alourdi le concept...

    J'ai maintenant 2 classes :
    -OPThread qui exécute un thread et gère le flux
    -Runnable qui implémente la méthode run() dans laquelle se trouve le métier du thread.
    Je transmets donc l'instance de Runnable à l'instanciation du OPThread et j'ai bien mon instance à passer à opus::bind pour lancer le thread.

    Pour le moment je m'en sors comme ça.

    Merci à tous pour votre aide !

  12. #12
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    juin 2008
    Messages
    7 636
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : juin 2008
    Messages : 7 636
    Points : 13 367
    Points
    13 367

    Par défaut

    Salut,

    Citation Envoyé par JolyLoic Voir le message
    Je ne sais pas ce qui chagrine 3DArchi, mais ce qui me chagrine moi, c'est que ces variables sont clairement destinées à être accédées depuis plusieurs threads, mais que l'on y accède sans aucun mécanisme de synchronisation.
    C'est ça. + typique des noms dédiés aux attentes actives. + typique des designs un peu bancales. On finit par sentir avec le nom d'une variable de futures longues heures de debuggage.

    Citation Envoyé par esteban Voir le message
    Pense-tu que cela puisse expliquer pourquoi il ne trouve pas la méthode run surchargée ?
    Ca peut effectivement assez bien expliquer le problème. La référence du shared_ptr tombe à 0, les destructeurs sont exécutés, donc la valeur vpointer reste sur la vtable du dernier destructeur utilisé i.e. de la classe de base.

Discussions similaires

  1. [XL-2010] Selection.find, comportement erratique?
    Par CoriS FrosT dans le forum Macros et VBA Excel
    Réponses: 5
    Dernier message: 22/11/2013, 11h23
  2. [PC fixe] Comportement erratique non lié à l'OS
    Par Arom77 dans le forum Ordinateurs
    Réponses: 4
    Dernier message: 06/01/2012, 17h36
  3. Comportements erratiques avec tp_attr : Effets de bord de Py_FindMethod ?
    Par Elenaher dans le forum Interfaçage autre langage
    Réponses: 0
    Dernier message: 23/06/2010, 15h59
  4. Comportement bizarre du "this" avec de l'héritage
    Par fcx35 dans le forum Débuter
    Réponses: 3
    Dernier message: 05/02/2009, 20h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo