Domanda

Ho scritto una libreria che espone riferimenti a diversi tipi di oggetti correlati. Tutti questi oggetti hanno la loro vita gestita internamente dalla biblioteca tramite boost::shared_ptr

Un utente della biblioteca sarebbe anche in grado di conoscere, per natura della biblioteca, la durata di vita di qualsiasi oggetto esposto. Quindi potrebbero archiviare puntatori o conservare riferimenti a questi oggetti. Sarebbe ragionevole per loro farlo e sapere quando quegli oggetti non sono più validi.

Ma mi sento in colpa costringendo i miei utenti a essere ragionevoli.

È accettabile che una libreria esponga weak_ptr ai suoi oggetti? Altre biblioteche l'hanno fatto?

Ho profilato l'utilizzo di questa libreria nelle app e l'ho trovato troppo critico per esporre <=> esclusivamente.

Sarebbe più saggio che le funzioni API corrispondenti espongano un riferimento o un debole_ptr o che qualsiasi oggetto sia in grado di esporre un <=> a se stesso?

È stato utile?

Soluzione

Se i smart_ptr sono già direttamente accessibili agli utenti della biblioteca, allora hanno già accesso ai weak_ptr, semplicemente tramite il costruttore del shared_ptr corrispondente. Ma se i <=> sono tutti interni alla libreria, questa è una storia diversa.

In tal caso, consiglierei di lasciare che ogni oggetto passi <=> se stesso, oltre a qualsiasi altro accesso offerto dalla tua biblioteca. Ciò offre agli utenti la massima flessibilità: se hanno bisogno di un <=>, hanno accesso immediato ad esso; se hanno bisogno di un <=>, possono facilmente ottenerlo; e se hanno solo bisogno di accedere all'oggetto stesso, possono ignorare del tutto i puntatori intelligenti.

Certo, non so cosa faccia la tua biblioteca o come sia usata o progettata. Ciò potrebbe cambiare la mia raccomandazione.

Altri suggerimenti

Venire con meccanismi contorti per raggiungere gli oggetti della tua biblioteca si tradurrà solo in persone che non usano la tua biblioteca. Se la semantica della libreria impone che le persone utilizzino weak_ptrs, non c'è modo per aggirare l'utente sapendo che gli oggetti potrebbero andare via ad un certo punto. Fai in modo che l'interfaccia esprima quante più informazioni possibili sull'uso della libreria, riduce la documentazione e semplifica infinitamente l'uso.

Non puoi progettare intorno a utenti cattivi / inesperti.

Se dai ai tuoi clienti l'accesso a weak_ptr loro possono semplicemente bloccarli per creare shared_ptr se finire per ritardare la distruzione degli oggetti. Ciò potrebbe causare problemi con la tua libreria.

Suggerisco di racchiudere un weak_ptr<T>::lock() in un'altra classe e di dare al chiamante un shared_ptr<InterfaceClass>. In questo modo non possono semplicemente chiamare <=>. Sembra che tu abbia vincoli di prestazione che potrebbero influenzare il modo in cui lo implementi, ma un <=> potrebbe essere un buon modo per andare e mantenere la classe con <=> interno alla tua libreria.

In questo modo mantieni anche questi dettagli di implementazione fuori dalla tua interfaccia della libreria e puoi cambiare il modo in cui lo implementi senza cambiare la tua interfaccia.

Non vedo alcun problema con l'esposizione di weak_ptrs, soprattutto dato che TR1 ha puntatori intelligenti simili (PDF).

TR1 è ampiamente implementato da Visual Studio e GCC, ma non alcuni degli altri compilatori disponibili. Ma quando viene implementato in tutti i compilatori che ti interessano, potresti voler rielaborare l'API per esporre invece quei puntatori intelligenti.

Se si desidera intercettare sia un utilizzo non valido della libreria (tentando di accedere agli oggetti dopo che sono stati eliminati) sia un'API ad alte prestazioni (non debole_ptr e shared_ptr nell'API), si potrebbe considerare di avere un'API diversa per build di debug e nondebug.

Supponiamo per semplicità che hai solo una classe di oggetti che esponi; chiama questa classe Object. Il tipo di puntatore che si restituisce dall'API per accedere agli oggetti interni viene quindi definito come:

#ifdef DEBUG
typedef ObjectPtrFacade ObjectPtr 
#else
typedef Object * ObjectPtr;
#endif

Qui la facciata è di classe che scrivi. Funziona più o meno in questo modo:

class ObjectPtrFacade {
public:
    ObjectPtrFacade(Object *o) : wptr(o) { }
    // copy constructor and assignment here etc. (not written)
    Object * operator -> () const { return access(); }
    Object & operator * () const { return *access(); }
private:
    Object * access() { 
      assert(wptr.use_count() > 0);
      return (Object *)(wptr.lock());
    }
    weak_ptr<Object> wptr; 
}

In questo modo, ogni volta che costruisci una build di debug, hai in uso un tipo speciale di puntatore intelligente che afferma prima di accedere all'oggetto che il suo use_count () è maggiore di zero, cioè che l'oggetto esiste ancora. Se l'oggetto è stato rilasciato, si ottiene un'asserzione non riuscita, che è meglio di un riferimento puntatore null.

In generale, ovviamente, è così che l'uso di weak_ptr non aiuta se hai " stupid " utenti dell'API, perché potrebbero chiamare lock () e quindi fare comunque riferimento a un puntatore null dopo che weak_ptr restituisce un file shared_ptr vuoto ...

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