web-dev-qa-db-fra.com

Quel est le chemin le plus court dans C++ 11 (ou plus récent) pour créer un wrapper RAII sans avoir à écrire une nouvelle classe?

Souvent, je suis dans une situation où j'ai besoin d'un simple wrapper RAII, mais je ne voudrais pas créer une toute nouvelle classe pour cela pour de nombreuses raisons, notamment des contraintes de temps et des problèmes d'organisation. Ma solution quick-n-dirty est la suivante.

Dites que je veux m'assurer que d'ici la fin de la portée, je veux qu'un booléen revienne à son état d'origine:

bool prevState = currState;
currState      = newState;
std::unique_ptr<int, std::function<void(int*)>> txEnder(new int(0), [&prevState](int* p) {
    currState = prevState;
    delete p;
});

Cette solution fonctionne bien, mais le problème est qu’il est nécessaire d’allouer et de désallouer cet entier pour que unique_ptr fonctionne et appeler le destructeur personnalisé à la destruction.

Existe-t-il un moyen plus simple de faire cela sans avoir à écrire toute une classe et à se débarrasser de la variable new pour le modèle factice int?

10

Un peu mieux que le vôtre: vous pouvez utiliser &prevState dans le destructeur personnalisé sans le supprimer. Vous n'avez donc pas besoin de new et delete

void foo(bool & currState, bool newState)
{
    bool prevState = currState;
    currState      = newState;
    std::unique_ptr<bool, std::function<void(bool*)>> txEnder(&prevState, [&prevState, &currState](bool* p) {
        currState = prevState;
    });
    cout << "currState: " << currState << endl;
}

Vous avez également oublié de capturer currState dans le lambda.

Voici un exemple: https://ideone.com/DH7vZu

6
mch

Vous pouvez utiliser BOOST_SCOPE_EXIT

auto prevState{currState};
currState = newState;
BOOST_SCOPE_EXIT(&currState, &prevState)
{
     currState = prevState;
} BOOST_SCOPE_EXIT_END
7
VTT

Que diriez-vous de gsl::finally? La bibliothèque n’est pas aussi lourde que boost et finally n’utilise pas std::function et peut donc être facilement insérée. Aussi, pas d'allocation dynamique de std::unique_ptr

using namespace std;

void foo(bool & currState, bool newState)
{
    auto revertState = gsl::finally([prevState = currState, &currState]{
        currState = prevState;
    });
    currState = newState;       
    cout << "currState: " << currState << endl;
}


int main() {
    bool state = false;
    foo(state, true);
    cout << "state: " << state << endl;
    return 0;
}

Exemple en ligne: https://ideone.com/Xi1izz (avec gsl::finally copié, puisque #include <gsl/gsl> n'est pas disponible ici)

0
R2RT

N'utilisez pas std::function. Cela crée beaucoup de code, y compris vtables. https://gcc.godbolt.org/z/XgDoHz
Si vous ne souhaitez absolument pas utiliser une classe ou une fonction externe, procédez comme suit:

bool foo_2() {
    bool f = false;
    auto eos = [&](void*){
        f = true;
    };
    std::unique_ptr<void, decltype(eos)> h{&eos,std::move(eos)};
    return f;
}

Si vous êtes d'accord avec une petite fonction réutilisable, ci-dessous fonctionne. Ceci résume le void* non utilisé. 

C++ 14 ou version ultérieure

template<class F>
auto call_at_end_of_scope(F&& f){
    auto eos = [f{std::forward<F>(f)}](void*){f();};
    return std::unique_ptr<void, decltype(eos)>{&eos,std::move(eos)};
}

bool foo_3() {
    bool f = false;
    auto handle = call_at_end_of_scope([&](){
        f = true;
    });
    return f;
}
0
balki