web-dev-qa-db-fra.com

suppresseur personnalisé lambda unique_ptr <T> pour la spécialisation des tableaux

J'ai récemment commencé à porter beaucoup de mon code d'application C++ existant vers C++ 11 et maintenant que je convertis vers les nouveaux pointeurs intelligents std :: unique_ptr et std :: shared_ptr, j'ai une question spécifique sur les suppresseurs personnalisés. Je veux ajouter un enregistreur lambda pour voir où mes suppressions sont appelées mais je ne parviens pas à compiler la version de spécialisation de la baie. Des conseils seraient très appréciés.

J'ai cherché en vain un exemple de suppresseur personnalisé pour la spécialisation de tableaux nique_ptr pour VC++ 1 ou GCC 4.5.2 +. Je voudrais imprimer un message de journal lorsque les suppresseurs sont appelés dans un lambda - principalement pour m'assurer que tous les pointeurs qui, selon moi, sont hors de portée le font. Est-ce possible pour la version tableau de la spécialisation? Je peux le faire fonctionner avec la version non tableau, et je peux également le faire fonctionner avec une spécialisation tableau si je passe une structure externe "MyArrayDeleter" comme deuxième argument. Encore une chose, serait-il possible de supprimer le laid std :: function car je pensais que je pouvais laisser la signature lambda le comprendre.

struct MySimpleDeleter {
    void operator()(int* ptr) const {
        printf("Deleting int pointer!\n");
        delete ptr;
    }
};
struct MyArrayDeleter {
    void operator()(int* ptr) const {
        printf("Deleting Array[]!\n");
        delete [] ptr;
    }
};
{
    // example 1 - calls MySimpleDeleter where delete simple pointer is called
    std::unique_ptr<int, MySimpleDeleter> ptr1(new int(5));

    // example 2 - correctly calls MyArrayDeleter where delete[] is called
    std::unique_ptr<int[], MyArrayDeleter> ptr2(new int[5]);

    // example 3 - this works (but default_delete<int[]> would have been passed
    // even if I did not specialize it as it is the default second arg
    // I only show it here to highlight the problem I am trying to solve
    std::unique_ptr<int[], std::default_delete<int[]>> ptr2(new int[100]);

    // example 3 - this lambda is called correctly - I want to do this for arrays
    std::unique_ptr<int, std::function<void (int *)>> ptr3(
        new int(3), [&](int *ptr){ 
            delete ptr; std::cout << "delete int* called" << std::endl; 
        });

    // example 4 - I cannot get the following like to compile
    // PLEASE HELP HERE - I cannot get this to compile
    std::unique_ptr<int[], std::function<void (int *)>> ptr4(
        new int[4], [&](int *ptr){  
            delete []ptr; std::cout << "delete [] called" << std::endl; 
        });
}

The compiler error is as follows:

The error from the compiler (which complains about the new int[4] for ptr4 below is:
'std::unique_ptr<_Ty,_Dx>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty,_Dx>'
1>          with
1>          [
1>              _Ty=int [],
1>              _Dx=std::tr1::function<void (int *)>
1>          ]
1>          c:\program files (x86)\Microsoft visual studio 10.0\vc\include\memory(2513) : see declaration of 'std::unique_ptr<_Ty,_Dx>::unique_ptr'
1>          with
1>          [
1>              _Ty=int [],
1>              _Dx=std::tr1::function<void (int *)>
1>          ]
30
johnco3

Qu'en est-il de:

auto deleter=[&](int* ptr){...};
std::unique_ptr<int[], decltype(deleter)> ptr4(new int[4], deleter);
36
Managu

Tout d'abord, j'utilise VC2010 avec SP1, Mingw g ++ 4.7.1

Pour le tableau nouveau, unique_ptr le prend déjà en charge de manière propre:

struct X
{
    X()   { puts("ctor"); }
   ~X()   { puts("dtor"); }
};

unique_ptr<X[]>  xp(new X[3]);

La sortie est:

ctor
ctor
ctor
dtor
dtor
dtor

Pour le deleter personnalisé, malheureusement, il est incohérent entre VC2010 et g ++:

VC2010:

  unique_ptr<FILE, function<void (FILE*)> > fp(fopen("tmp.txt", "w"), [](FILE *fp){
    puts("close file now");
    fclose(fp);
  });

g ++:

  unique_ptr<FILE, void (*)(FILE*) > fp(fopen("tmp.txt", "w"), [](FILE *fp){
    puts("close file now");
    fclose(fp);
  });

La méthode de Managu est très bien, car lambda en ligne est cool mais blessé à la lisibilité à mon humble avis. Il met également l'accent sur cette ressource de publication avant l'acquisition (RAII).

Ici, je suggère une méthode déclartive pour séparer l'acquisition et la libération des ressources (Scope Guard, fonctionne à la fois pour VC2010 et g ++ 4.7.1):

template<typename T>
struct ScopeGuard
{
    T deleter_;
    ScopeGuard( T deleter) : deleter_(deleter) {}
    ~ScopeGuard() { deleter_() ; }
};
#define UNI_NAME(name, line) name ## line
#define ON_OUT_OF_SCOPE_2(lambda_body, line) auto UNI_NAME(deleter_lambda_, line) = [&]() {    lambda_body; } ; \
       ScopeGuard<decltype(UNI_NAME(deleter_lambda_, line))> \
       UNI_NAME(scope_guard_, line)  ( UNI_NAME(deleter_lambda_, line ));
#define ON_OUT_OF_SCOPE(lambda_body) ON_OUT_OF_SCOPE_2(lambda_body, __LINE__)

FILE * fp = fopen("tmp.txt", "w");
ON_OUT_OF_SCOPE( { puts("close file now"); fclose(fp); } );

Le fait est que vous pouvez obtenir une ressource de façon claire et ancienne et déclarer l'instruction de libérer la ressource immédiatement après la ligne d'acquisition de ressources.

L'inconvénient est que vous ne pouvez pas transmettre un seul objet avec son deleter.

Pour FILE *, shared_ptr peut être utilisé comme pointeur alternatif dans le même but (peut-être un peu lourd, mais fonctionne bien pour VC2010 et g ++)

shared_ptr fp2 (fopen ("tmp.txt", "w"), [] (FILE * fp) {fclose (fp); put ("close file");});

4
zhaorufei