Est-il sage de fournir un accès à faible_ptr dans une interface de bibliothèque?

StackOverflow https://stackoverflow.com/questions/631380

  •  08-07-2019
  •  | 
  •  

Question

J'ai écrit une bibliothèque qui expose les références à plusieurs types d'objet liés. La durée de vie de tous ces objets est gérée par la bibliothèque en interne via boost::shared_ptr

Un utilisateur de la bibliothèque pourrait également connaître, de par sa nature, la durée de vie de n’importe quel objet exposé. Ils pourraient donc stocker des pointeurs ou des références à ces objets. Il serait raisonnable pour eux de le faire et de savoir quand ces objets ne sont plus valables.

Mais je me sens coupable d'avoir forcé mes utilisateurs à être raisonnables.

Est-il acceptable d'avoir une bibliothèque exposant les weak_ptr objets à ses objets? Est-ce que d'autres bibliothèques l'ont fait?

J'ai décrit l'utilisation de cette bibliothèque dans les applications et j'ai trouvé qu'il était trop critique pour exposer <=> en exclusivité.

Serait-il plus sage de faire en sorte que les fonctions d'API correspondantes exposent soit une référence ou un faible_ptr, soit de rendre tout objet capable de s'exposer <<> à lui-même?

Était-ce utile?

La solution

Si les smart_ptr s sont déjà directement accessibles aux utilisateurs de la bibliothèque, ils ont déjà accès aux weak_ptr s, simplement via le constructeur de shared_ptr 's correspondant. Mais si les <=> sont tous internes à la bibliothèque, il en va tout autrement.

Dans ce cas, je vous recommande de laisser chaque objet passer <=> à lui-même, en plus de tout autre accès offert par votre bibliothèque. Cela donne aux utilisateurs le maximum de flexibilité: s’ils ont besoin d’un <=>, ils y ont un accès immédiat; s'ils ont besoin d'un <=>, ils peuvent facilement l'obtenir; et s'ils ont juste besoin d'accéder à l'objet lui-même, ils peuvent ignorer complètement les pointeurs intelligents.

Bien sûr, je ne sais pas ce que fait votre bibliothèque, ni comment elle est utilisée ou conçue. Cela pourrait changer ma recommandation.

Autres conseils

Si vous utilisez des mécanismes compliqués pour accéder aux objets de votre bibliothèque, seuls les utilisateurs ne l'utilisant pas. Si la sémantique de la bibliothèque vous dicte de demander aux utilisateurs d’utiliser faibles, il n’existe aucun moyen de contourner l’utilisateur, sachant que les objets risquent de disparaître à un moment donné. Faites en sorte que l'interface exprime autant d'informations que possible sur l'utilisation de la bibliothèque, conserve la documentation et la rend infiniment plus simple d'utilisation.

Vous ne pouvez pas concevoir autour d'utilisateurs mal / inexpérimentés.

Si vous permettez à vos clients d'accéder à weak_ptr s, ils peuvent simplement les verrouiller pour créer shared_ptr s et retarder la destruction d'objets. Cela pourrait causer des problèmes avec votre bibliothèque.

Je suggère d’envelopper un weak_ptr<T>::lock() dans une autre classe et de donner à l'appelant un shared_ptr<InterfaceClass>. De cette façon, ils ne peuvent pas simplement appeler <=>. Vous semblez avoir des contraintes de performances qui peuvent influer sur la façon dont vous l'implémentez, mais un <=> peut être un bon moyen de continuer et de conserver la classe avec le <=> interne à votre bibliothèque.

De cette manière, vous gardez également ces détails d'implémentation en dehors de l'interface de votre bibliothèque et vous pouvez changer la façon dont vous l'implémentez sans changer d'interface.

Je ne vois pas de problème à exposer les faibles, surtout que TR1 possède des pointeurs intelligents similaires (PDF).

TR1 est largement implémenté par Visual Studio et GCC, mais pas par d’autres compilateurs. Toutefois, une fois implémenté dans tous les compilateurs qui vous intéressent, vous voudrez peut-être retravailler l’API pour exposer ces pointeurs intelligents à la place.

Si vous souhaitez à la fois intercepter une utilisation non valide de la bibliothèque (essayer d'accéder aux objets après leur suppression) et disposer d'une API hautes performances (pas de faiblesse et de shared_ptr dans l'API), vous pouvez envisager de une autre API pour les versions debug et nondebug.

Supposons, pour simplifier, que vous n’exposiez qu’une seule classe d’objets; appelez cette classe Object. Le type de pointeur renvoyé par l'API pour accéder aux objets internes est ensuite défini comme suit:

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

Ici, la façade est la classe que vous écrivez. Cela fonctionne à peu près comme ceci:

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; 
}

De cette manière, chaque fois que vous créez une version de débogage, vous utilisez un type spécial de pointeur intelligent qui affirme, avant d'accéder à l'objet, que use_count () est supérieur à zéro, c'est-à-dire que l'objet existe toujours. Si l'objet a été publié, vous obtenez une assertion qui échoue, ce qui est préférable à une référence de pointeur null.

En général, bien sûr, il est préférable que l’utilisation de faibles_ptr n’aide pas si vous avez & "stupide &"; les utilisateurs de l'API, car ils peuvent appeler lock () puis créer une référence null-pointeur une fois que le module faible_ptr renvoie un code partagé qui est vide ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top