Domanda

Sto usando C ++ con la libreria OpenCV, che è un'elaborazione delle immagini della libreria sebbene non sia rilevante per questa domanda. Attualmente ho una decisione di progettazione da prendere.

OpenCV, essendo una libreria C, ha le sue strutture di dati (come CvMat) dichiarate come strutture. Per crearli, usi funzioni come cvCreateMat e per rilasciarli, usi funzioni come cvReleaseMat. Essendo un programmatore C ++, ho creato una classe speciale cv_scoped che chiamava automaticamente cvReleaseMat quando usciva dall'ambito (come boost::scoped_ptr).

Quello che sto realizzando ora è che vorrei poter usare auto_ptr e shared_ptr anche nei casi. Sento solo che scrivere codice per le mie classi cv_auto_ptr e cv_shared_ptr sarebbe una cattiva idea, per non parlare della perdita di tempo. Quindi ho cercato soluzioni e ho trovato tre possibilità.

Prima , potrei usare la classe cv_scoped che ho già creato. Lo rinominerei in cv_ptr e quindi utilizzerei i puntatori intelligenti in questo modo: std::auto_ptr<cv_ptr>. La cosa fastidiosa di questo è che dovrei sempre dereference due volte:

std::auto_ptr<cv_ptr> matrix(cv_ptr(cvCreateMat(320, 240, CV_32FC3)));
cvPow(matrix.get()->get()); // one get for the auto_ptr, one for the cv_ptr

So che potrei dichiarare una conversione implicita, ma in realtà non potrei - la maggior parte delle funzioni di OpenCV hanno il parametro void * - quindi nessuna conversione implicita sarebbe chiamata. Mi piacerebbe davvero un modo per farlo in cui non dovevo fare la doppia dereferenza.

Secondo , potrei in qualche modo sovrascrivere operator delete. Non voglio sovrascrivere la cancellazione dell'operatore globale perché vorrei che questo si applica solo ai tipi CvMat (e pochi altri). Tuttavia, non posso cambiare la libreria, quindi non posso aggiungere scoped_ptr alla struttura CvMat. Quindi non so come funzionerebbe.

Terzo , potrei semplicemente riscrivere i miei <=>, <=> e <=>. Non sono classi di grandi dimensioni, quindi non sarebbe troppo difficile, ma penso che questo sia un cattivo design. Se dovessi farlo, probabilmente farei qualcosa del genere:

class cv_auto_ptr {
public:
  cv_auto_ptr();
  ~cv_auto_ptr();

  // each method would just be a proxy for the smart pointer
  CvMat* get() { return this->matrix_.get()->get(); }
  // all the other operators/methods in auto_ptr would be the same, you get the idea

private:
  auto_ptr<cv_ptr> matrix_; // cv_ptr deletes CvMat properly
}

Cosa faresti nella mia situazione? Per favore, aiutami a capire questo.

È stato utile?

Soluzione

Gli auto_ptr sono davvero progettati per RAII su classe C ++ con costrutti / distruttori che stai spingendo i loro usi qui a cose per cui probabilmente non dovrebbero essere usati (ma possono).

In ogni caso, non vuoi essere in grado di usare il tuo oggetto C ++ come se fosse una normale variabile di stack senza allocazione dinamica ogni volta?

La soluzione standard al tuo problema è quella di creare un wrapper con costruttore / distruttore.
Ma per renderlo utilizzabile dalle funzioni C è sufficiente aggiungere un operatore di cast interno in modo che si converta automaticamente in oggetto C quando viene passato a una funzione C

Scrivi una classe wrapper.

class Mat
{
    CvMat* impl;
    public:
        Mat(/* Constructor  Arguments */)
        {
            impl = cvCreateMat(/* BLAH */);
        }
        ~Mat()
        {
            cvReleaseMat(impl);
        }
        operator CvMat*()
        {   // Cast opertator. Convert your C++ wrapper object into C object
            // when you use it with all those C functions that come with the
            // library.

            return impl;
        }
};

void Plop(CvMat* x)
{   // Some C function dealing with CvMat
}

int main()
{                            // Don't need to dynamically allocate
    Mat                  m;  // Just create on the stack.
    Plop(m);                 // Call Plop directly

    std::auto_ptr<Mat>   mP(new Mat);
    Plop(*mP);
}

Altri suggerimenti

Un approccio che potresti prendere in considerazione è quello di utilizzare il fatto che std::tr1::shared_ptr ha le funzionalità per fornire un deleter personalizzato. Non ho familiarità con OpenCV, quindi desumo da ciò che hai scritto.

struct CvMatDeleter
{
    void operator( CvMat* p ) { cvReleaseMat( p ) ; }
};

void test()
{
    std::tr1::shared_ptr< CvMat > pMat( cvCreateMat(320, 240, CV_32FC3), CvMatDeleter() );
    // . . .
}

Poiché il deleter è memorizzato nel puntatore condiviso, è possibile utilizzarlo normalmente e quando il puntatore non elaborato condiviso deve essere eliminato, cvReleaseMat verrà chiamato come richiesto. Nota che auto_ptr e scoped_ptr sono classi molto più leggere, quindi non hanno la funzionalità per i deleter personalizzati, ma se sei pronto per il piccolo overhead allora shared_ptr può essere usato al loro posto.

Se tutto ciò che ti interessa è la sicurezza delle eccezioni, fallo ogni volta che usi le matrici:

void f() {
    try {
        CvMat* mat = cvCreateMat(320, 240, CV_32FC3));
        // ...
    } catch(...) {
        cvReleaseMat(mat);
        throw;
    }
    cvReleaseMat(mat);
}

Se invece vuoi una soluzione sana , fai il possibile e scrivi un wrapper completo.

namespace cv {

class Mat {
public:
    enum Type { /* ... */ };
    Mat(int w, int h, Type type) {
        impl = cvCreateMat(w, h, intFromType(type));
    }

    ~Mat() {
        cvReleaseMat(impl);
    }

    void pow() { // wrap all operations
        cvPow(impl);
    }

private:
    CvMat* impl;
};

}

Andando a metà strada, usando un hodge podge di generici puntatori intelligenti e " cv_ptrs " suona come una ricetta per il mal di testa e una complicazione inutile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top