Gibt es ein geeignetes „Ownership-in-a-Package“ für „Griffe“?

Lesezeit: 6 Minuten

Benutzer-Avatar
pfeffer_chico

Griffe haben eine andere richtige Semantik als Zeiger. Also für mich ein Beispiel wie dieses (aus der Nullregel):

class module {
public:
    explicit module(std::wstring const& name)
    : handle { ::LoadLibrary(name.c_str()), &::FreeLibrary } {}

    // other module related functions go here

private:
    using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;

    module_handle handle;
};

verwenden unique_ptr als „Ownership-in-a-Package“ für Griffe ist ein schlechtes Beispiel. Erstens nutzt es das interne Wissen, dass das Handle ein Zeigertyp ist, und verwendet dies, um a zu erstellen unique_ptr auf den Grundtyp baut der Grifftyp „blickdicht“ auf.

Handles können von jedem Typ sein, sie können ein Zeiger sein, sie können ein Index sein oder wer weiß. Am wichtigsten ist, dass Sie (von den meisten C-APIs zum Beispiel) ein Handle und seine Ressourcenfreigabefunktion zur Hand haben.

Gibt es dafür ein richtiges „Ownership-in-a-Package“. funktioniert in Handle-Semantik? Ich meine, bereits öffentlich verfügbar, damit man es verwenden kann?

Für mich, unique_ptr et. Al. nicht funktioniert, muss ich unnötige Annahmen über den Handle-Typ treffen istwenn ich nur durch den undurchsichtigen Grifftyp und seine Freigabefunktion nur ein “Eigentum an einem Paket” erhalten möchte.

Es macht keinen Sinn, in den Handle-Typ zu blicken, um Konstruktionen auf dieser Information zu erstellen. Es ist ein Griff, es sollte keine Rolle spielen.

Ich werde hier die Gefühle eines anderen SO-Benutzers in der Antwort einer anderen Frage zitieren:

Erstellen Sie eine bestimmte “Smart Pointer” -Klasse, das dauert nicht lange. Missbrauche den Bibliotheksunterricht nicht. Handle-Semantik unterscheidet sich stark von der eines C++-Zeigers; Zum einen macht die Dereferenzierung eines HANDLE keinen Sinn.

Ein weiterer Grund, eine benutzerdefinierte Smart-Handle-Klasse zu verwenden – NULL bedeutet nicht immer ein leeres Handle. Manchmal ist es INVALID_HANDLE_VALUE, was nicht dasselbe ist.

Haftungsausschluss:

Diese Frage formuliert diese neu und baut darauf auf:

  • Wo ist die richtige (Ressourcenhandhabung) Rule of Zero?

  • Ich bin mit Ihrer Prämisse nicht einverstanden. unique_ptr ist vielleicht eine leichte Fehlbezeichnung – es handhabt Ressourcen. EIN HANDLE kümmert sich auch um Ressourcen. Dies ist eine perfekte Übereinstimmung.

    – Konrad Rudolf

    14. Februar 2013 um 15:27 Uhr


  • Ich stütze meine Prämissen nicht nur auf den Typnamen …

    – pfeffer_chico

    14. Februar 2013 um 15:29 Uhr

  • Was ist genau dein problem? Der Name gefällt dir nicht unique_ptr?

    – Etienne de Martell

    14. Februar 2013 um 15:32 Uhr


  • @ EtiennedeMartel: Zu verwenden unique_ptr Für ein Nicht-Zeiger-Handle benötigen Sie einen speziellen Löscher, der eine Verschachtelung definiert pointer Typ, und das pointer Typ kann nicht einfach sein, sagen wir, intseit int entspricht nicht den NullablePointer-Anforderungen, die unique_ptr will. Jedoch, Sie können einen einfachen Wrapper schreiben das passt sich an irgendetwas zu diesen Nullable-Pointer-Anforderungen. Wenn Sie möchten, können Sie auch ein Unär hinzufügen operator* das leitet weiter *valueund ein operator-> damit du es wirklich verwenden kannst *up und up->foo().

    – Xeo

    14. Februar 2013 um 15:34 Uhr


  • @Xeo: Wenn Sie sich die Mühe machen, diesen Wrapper zu schreiben … warum schreiben Sie nicht einfach ein richtiges RAII-Objekt, das einen beliebigen Wert enthalten kann, und rufen eine bestimmte Funktion / einen bestimmten Funktor für diesen Typ auf, wenn er zerstört wird? Es ist verdammt viel weniger stumpf als mit unique_ptr für Nicht-Zeiger-Dinge. Im Ernst, Schreiben von Copy/Move-Konstruktoren für ein Objekt ist kaum eine lästige Belastung.

    – Nicol Bolas

    15. Februar 2013 um 3:23 Uhr


Der Typ unique_ptr ist weniger allgemein als der Ausdruck “Handle”, ja. Aber warum sollte es nicht sein? Nur eines Ihrer “Handle”-Beispiele (z. B. dasjenige, das ein ganzzahliger Index ist) ist genau so allgemein wie unique_ptr. Sie können eine bestimmte Art von Griff nicht mit “allen Griffen aller Zeiten” vergleichen.

Wenn Sie einen einzelnen, konkreten C++-Typ (oder eine Typvorlage) wünschen, der ein Handle ist, ohne tatsächlich eine bestimmte Behandlungssemantik zu definieren, dann … kann ich Ihnen nicht helfen. Ich glaube nicht, dass irgendjemand es nachvollziehbar könnte.

  • Es ist weniger allgemein und sollte daher dort verwendet werden, wo es passt. Wenn man anfängt, was nachzuschlagen HANDLE (angeblich undurchsichtig, eines der Hauptmerkmale von Griffen) darauf aufgebaut ist, und diese Informationen in dem weniger allgemeinen Werkzeug zu verwenden, für mich ist das ein schlechter Geruch. Natürlich kann man behaupten HANDLE ist void * Was ist in meinem Fall mit anderen Handle-basierten Bibliotheken, werden Sie diese Behauptung immer annehmen? Und diese Annahme ist nicht Teil der Handle-Semantik, HANDLE ist undurchsichtig.

    – pfeffer_chico

    14. Februar 2013 um 15:52 Uhr


  • @chico: “Andere Handle-basierte Bibliotheken” haben unterschiedliche Handle-Typen. unique_ptr ist ein Grifftyp. Was tatsächlich Problem treten Sie in Ihrer Codierung auf?

    – Leichtigkeitsrennen im Orbit

    14. Februar 2013 um 16:09 Uhr


  • unique_ptr ist ein Grifftyp? Ich weiß nicht, was du damit meinst. HANDLE ist a handhaben Typ. Mein eigentliches Problem ist Ignorieren ist undurchsichtig Gebrauch machen von unique_ptr um die Lebensdauer der Ressource zu verwalten, auf die sie sich bezieht. Ich betrachte diese schlechte Praxis und frage nach einer bekannten “Ownership-in-a-Package” -Alternative, die es richtig macht. Mit Recht meine ich, nicht solche Verletzungskapselung. Handle-Typen undurchsichtig sind und ein gutes Werkzeug handhaben würde Griffeendlich ganzzahlig und zeigerbasiert, die gleiche syntaktische Weise.

    – pfeffer_chico

    14. Februar 2013 um 17:37 Uhr


  • @chico: Sie sagen immer wieder, dass Handle-Typen undurchsichtig sein müssen, aber ich sehe keinen Grund dafür. unique_ptr tut nur gut für ein Handle mit Zeigersemantik. Sicher, es hat keine Nicht-Zeiger-Semantik. Es ist ein Art Griff.

    – Leichtigkeitsrennen im Orbit

    14. Februar 2013 um 17:42 Uhr

  • Es spielt keine Rolle, ob man keinen Grund sieht, Griffe als undurchsichtig anzunehmen, entscheidend ist, dass sie allgemein als solche angenommen werden. Sie können Ihren Grifftyp als bauen void * intern und versichern Sie Ihren Kunden im Voraus, dass dies immer so sein wird. Ihre Clients können dann sicher verwendet werden unique_ptr<void> damit (schau das unique_ptr<void> macht nicht wirklich Sinn, man kann aus einem Handle-Typ nichts ableiten, es ist kein Zeiger auf untyped). Dies ist jedoch bei Ihrem speziellen Griff der Fall. Dieser Prozess ist im Allgemeinen nicht für Griffe geeignet.

    – pfeffer_chico

    14. Februar 2013 um 17:50 Uhr


Benutzer-Avatar
pfeffer_chico

std::experimental::unique_resource

  • Warum den Client zwingen, manuell eine Bereinigung bereitzustellen (d. h. das Lambda, das an make_scoped_resource) an den Einsatzorten?

    – R.Martinho Fernandes

    16. Februar 2013 um 16:22 Uhr


  • @R.MartinhoFernandes Ist das nicht der Fall? unique_ptr mit deleter? Und wenn Sie default_deleter in der Handle-Semantik verwenden, ist das sinnvoll? Ich denke, in den meisten Fällen erhalten Sie damit eine kürzere Syntax, und Sie haben die std::unique_resource Option, die das nicht erzwingt. Aber trotzdem frage ich mich nach einer guten Lösung dafür.

    – pfeffer_chico

    16. Februar 2013 um 16:53 Uhr

  • Nicht, wenn Ihr Deleter standardmäßig konstruierbar ist. Der Punkt ist, dass make_scoped_resource, genau wie der ctor von unique_ptr, der einen Deleter verwendet, niemals im Client-Code verwendet werden sollte. Es sollte nur in Bibliotheksfunktionen verwendet werden. Etwas wie make_gl_listdas intern vielleicht verwendet make_scoped_resourceaber zwingt den Kunden nicht, sich darum zu kümmern.

    – R.Martinho Fernandes

    16. Februar 2013 um 16:55 Uhr


  • @R.MartinhoFernandes Warum einen Sprachbenutzer zwingen, sich in alle Situationen einzuwickeln, in denen Handles in der Verwendung von Legacy-APIs vorkommen? Eine Bibliotheksfunktion muss möglicherweise einmal für eine Operation eine Legacy-API aufrufen. Warum das Umschließen für eine so triviale Situation erzwingen? Es ist eine übliche Verwendung für Leute, die mit C arbeiten, warum nicht gute Ressourcen bereitstellen, um damit umzugehen? Kleine Wrapper, private Wrapper-Bibliotheken mit ähnlicher Funktionalität, Code-Tricks, all das muss weg!

    – pfeffer_chico

    16. Februar 2013 um 17:14 Uhr

  • Was. In Ihrem Codebeispiel der Benutzer selbst muss Umgang mit der Legacy-API (siehe dort den Aufruf von glDeleteLists?). Das soll weg.

    – R.Martinho Fernandes

    16. Februar 2013 um 17:33 Uhr


1012430cookie-checkGibt es ein geeignetes „Ownership-in-a-Package“ für „Griffe“?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy