3 votes

C++: Un objet std::runtime_error va-t-il fuir dans un longjmp?

Supposons que j'ai du code C++ qui a un bloc try-catch dans lequel la partie catch déclenchera un long saut:

#include 
#include 
#include 

void my_fun()
{
    jmp_buf jump_buffer;
    if (setjmp(jump_buffer))
        return;

    try {
        std::string message;
        message.resize(100);
        snprintf(&message[0], 100, "error code %d\n", 3);
        throw std::runtime_error(message);
    }

    catch (std::runtime_error &e) {
        longjmp(jump_buffer, 1);
    }
}

Étant donné que l'objet std::runtime_error a été alloué dynamiquement quelque part, est-ce que la mémoire qui lui a été allouée ou pour la chaîne de caractères sera libérée?

7voto

Nicol Bolas Points 133791

C'est un peu compliqué. À propos de la validité de longjmp, le standard dit:

Une paire d'appels setjmp/longjmp a un comportement indéfini si le remplacement de setjmp et longjmp par catch et throw invoquerait des destructeurs non triviaux pour des objets avec une durée de stockage automatique.

runtime_error a un destructeur non trivial, donc la question est de savoir si l'objet exception a une "durée de stockage automatique". Ce n'est pas le cas. Cela suggère que longjmp devrait fonctionner correctement.

De plus, la destruction de l'objet exception peut se produire à l'un de deux endroits:

Les points de destruction potentiels pour l'objet exception sont:

  • lorsqu'un gestionnaire actif pour l'exception se termine de toute autre manière que par un rethrow, immédiatement après la destruction de l'objet (le cas échéant) déclaré dans la déclaration d'exception dans le gestionnaire;

  • lorsqu'un objet de type std::exception_ptr qui fait référence à l'objet exception est détruit, avant que le destructeur de std::exception_ptr ne retourne.

longjmp n'est pas un "rethrow". Donc en théorie, cela devrait fonctionner grâce au point 1.

Cela étant dit, ne jamais se fier à cela. Je doute fortement que les implémentations de longjmp gèrent correctement cela, et même si certaines le font, ce n'est probablement pas quelque chose que vous pouvez attendre.

4voto

Andrew Pinski Points 41

Il existe actuellement un rapport de bogue C++ concernant ce cas spécifique, http://wg21.link/cwg2361. Donc pour l'instant, il est difficile de dire ce qui est correct.

2voto

Pepijn Kramer Points 243

Pourquoi vous voudriez même faire quelque chose comme ça est au-delà de moi. Mais la question était intrigante. Je sais que vous ne pouvez pas sauter dans un bloc catch, et qu'il est permis de sauter en dehors de celui-ci (également goto :( ).

Il semble que l'exception soit détruite juste après avoir quitté la portée du catch. Et rien ne fuit. Voici la sortie du programme de test ci-dessous :

portée : normal_try_catch::try entrée
exception construite
portée : normal_try_catch::try quittée
portée : normal_try_catch::catch entrée
portée : normal_try_catch::catch quittée
exception détruite
----------------------------
portée : long_jump_catch::try entrée
exception construite
portée : long_jump_catch::try quittée
portée : long_jump_catch::catch entrée
portée : long_jump_catch::catch quittée
exception détruite
portée : long_jump_catch::leave en raison de setjmp entrée
portée : long_jump_catch::leave en raison de setjmp quittée

Donc l'exception est nettoyée avant que le saut ne soit effectué.

#include 
#include 
#include 
#include 

class my_except :
    public std::exception
{
public:
    my_except() 
    {
        std::cout << "exception construite" << std::endl;
    }

    ~my_except()
    {
        std::cout << "exception détruite" << std::endl;
    }
};

struct scope
{
    explicit scope(const std::string& scope) :
        m_scope{ scope }
    {
        std::cout << "portée : " << m_scope << " entrée" << std::endl;
    }

    ~scope()
    {
        std::cout << "portée : " << m_scope << " quittée" << std::endl;
    }

private:
    std::string m_scope;
};

void normal_try_catch()
{
    try
    {
        scope s{ "normal_try_catch::try" };
        throw my_except();
    }
    catch(const std::exception&)
    {
        scope s{ "normal_try_catch::catch" };
    }
}

void long_jump_catch()
{
    jmp_buf jump_buffer;
    if (setjmp(jump_buffer))
    {
        scope s{ "long_jump_catch::leave en raison de setjmp" };
        return;
    }

    try
    {
        scope s{ "long_jump_catch::try" };
        throw my_except();
    }
    catch (const std::exception&)
    {
        scope s{ "long_jump_catch::catch" };
        longjmp(jump_buffer, 1);
    }
}

int main()
{
    normal_try_catch();
    std::cout << "----------------------------" << std::endl;
    long_jump_catch();
    return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X