[C++] teure Ressource in Lambda moven

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Schrompf
Moderator
Beiträge: 5077
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

[C++] teure Ressource in Lambda moven

Beitrag von Schrompf »

Hallo,

ich habe mal wieder ein C++-Problem. Ich habe ein Job-System, dem ich beliebige Callables zur Ausführung in diversen Threads übergeben kann. Das sieht in etwa so aus:

Code: Alles auswählen

void Dings::MachParallel( std::function<void()>&& func);
Das funktioniert soweit prima, solange ich normale Lambdas verwende. Nun möchte ich aber eine dicke Ressource in das Lambda hinein moven, also dem Lambda den Ownership übergeben. Innerhalb des Lambdas soll das Objekt dann nochmal in eine Funktion gemoved werden. Etwa so:

Code: Alles auswählen

auto lambda = [zeugs = std::move(dieRessource)]() mutable { MachWasDamit( std::move( zeugs)); };
dings.MachParallel( std::move( lambda));
Das Lambda muss mutable sein, weil der normale operator() ja eigentlich const wäre und ich die Ressource damit nicht weiterverschieben könnte. Das Problem ist nun, dass ich eine Wand aus Compilerfehlern bekomme, die anscheinend bedeuten, dass std::function irgendwie mein Lambda kopieren will. Und das geht natürlich nicht, weil mein Ressourcen-Objekt nicht kopierbar, sondern nur movable ist.

Zum Ausprobieren gibt hier Beispielcode: https://godbolt.org/z/xxDBIW

Die Frage an die Experten in der Runde lautet: was ist die idiomatische Lösung, um teure Ressourcen mit Ownership an einen Threadworker zu übergeben? Danke im Voraus!
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: [C++] teure Ressource in Lambda moven

Beitrag von Spiele Programmierer »

Das Problem ist, dass man std::function<> selbst kopieren kann. Und um jetzt die std::function<> kopieren zu können, muss das zugewiesene Lambda natürlich auch kopierbar sein. Das ist nunmal Teil des Interfaces. Du könntest dein Funktionsobject ja z.B. in eine andere Funktion he­r­ein­rei­chen. Spätestens dort kann man ja wirklich nicht mehr wissen, dass man genau dein Funktionsobjekt jetzt nicht kopieren darf.

Da hilft wohl nur std::make_shared beim Zuweisen oder eine eigene Funktionszeigerklasse.

Sonst mir noch ein, man könnte eine Wrapperklasse schreiben wo das Move-Only Objekt z.B. in einem optional<> vorliegt und der Kopierkonstruktor über Move implementiert ist. Das sollte gehen, aber idiomatisch würde ich das nicht nennen. (Und wenn man ganz, ganz, ganz streng ist, auch nicht standardkonform. Was wenn std::function<> intern Kopien anlegen würde?)
Zuletzt geändert von Spiele Programmierer am 01.10.2018, 18:00, insgesamt 1-mal geändert.
Benutzeravatar
Schrompf
Moderator
Beiträge: 5077
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] teure Ressource in Lambda moven

Beitrag von Schrompf »

Tja, diverse kluge Menschen von StackOverflow sagen das Gleiche wie Du: den Lambda in nen std::shared_ptr moven und den dann als kopierbares Objekt in nen normalen Lambda packen.

Der Hintergrund ist, wie ich gerade erst verstanden habe: std::function<void()> ist ein abstrakter Typ, der muss für alle Instanzen die Eigenschaften festlegen. Und std::function sagt halt: "ich bin kopierbar". Mein Lambda ist nicht kopierbar wegen des nicht kopierbaren Members. std::function kann aber keine Ausnahme für meinen Spezialbruder machen, weil es halt beliebige Funktoren fressen kann. Tja, dumm gelaufen.

Ich werde aber wohl eher ne kleine Cheater-Struktur bauen, die zwar Kopieren erlaubt, aber dann zur Laufzeit asserted oder so. Ich möchte an der Stelle nicht allokieren, weil ich dort kein Lock gebrauchen kann.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: [C++] teure Ressource in Lambda moven

Beitrag von Spiele Programmierer »

Hast du denn sichergestellt, dass deine std::function<> nicht allokiert?
Die übernimmt Ownership von deinem Lambda und wenn das zu groß ist, wird auch allokiert.
Beim MSVC ab einer Pointergröße und bei Clang/GCC mit libstdc++ ab 2 Pointergrößen im Captureblock. (Quelle: Rumprobieren auf dem Compiler Explorer)

EDIT: Mit make_shared wären es auf Windows also mindestens zwei Allokationen...
Eine typische Zero-Cost Abstraction also. :roll:
Antworten