2016-06-30 13 views
3

Ich habe ein interessantes Problem mit C++ Lambdas. Der Use Case ist ein performance-kritischer Thread, der ein Lambda mit nicht-leerem Closure erstellt, das dann an einen anderen Thread übergeben wird, damit es "bearbeitet" werden kann.Platzierung neu für Lambda Capture

Die allgemeine Idee funktioniert, aber meine Lambda Schließung Objekt wird eine zusätzliche Zeit kopiert und Compiler (derzeit g ++ 5.4.0) ist nicht in der Lage, diese Kopie einschließlich Kopie Elision weg optimieren. Ich habe auch versucht, mit ähnlichem Ergebnis zu klirren.

Im Idealfall würde Ich mag eine Platzierung neu verwenden, wenn Lambda Erstellung direkt in den dauerhaften Speicherobjekte anstelle von ersten Erstellen von Objekt auf Stapel zum Beispiel:

auto l = new (buf) [a,b,c](){} 

Der Fehler ist so etwas wie:

lambda_ctor.cpp:39:19: error: expected type-specifier before '[' token 
    auto al = (buf) new [a,b,c](){}; 

Ich dachte, vielleicht, wenn ich einen Typ in der Hand anstelle von tatsächlichen Lambda-Ausdruck hatte, sollte Compiler es akzeptieren, aber zu versuchen, Art der Schließung zu heben, ohne es zu bewerten läuft in ein anderes Problem:

using T = decltype([a,b,c](){}); 

mit diesem Fehler:

lambda_ctor.cpp:41:24: error: lambda-expression in unevaluated context 
    using T = decltype([a,b,c](){}); 

ich nicht einen Weg zu finden, um diese scheinen kann, gibt es eine Reihe von Ideen, wie am Umfang Ausgang Lambda halten von zerfall aber jede Lösung scheint zu beinhalten Kopieren/Verschieben des Closure-Objekts, nachdem es erstellt wurde (auch als Extra-Kopie bezeichnet). Dies ist ein wenig seltsam, da wir normalerweise die volle Kontrolle darüber haben, wie ein benutzerdefinierter Funktor erstellt und weitergegeben wird und ähnliche Regeln für Closures gelten sollten.

+0

verwandt/Betrogene: http://stackoverflow.com/questions/37924996/c11-lambda-with-dynamic-storage-duration – NathanOliver

+0

Warum nicht eine aufrufbare Struktur machen (functor)? – Galik

+0

Verwenden Sie 'std :: function' als Lambda-Container – fnc12

Antwort

2

auto ist dein Freund.

auto l = new (buf) auto([a,b,c](){}); 

Jede Kopie des Closure-Objekts sollte elidierbar sein.

+0

Dies ist genau das, was ich mir erhofft habe, es wurde keine Kopie erstellt und die Closing-Mitglieder wurden an Ort und Stelle initialisiert, vielen Dank! – rtz

+0

Wann wurde das hinzugefügt? 14 oder 17? Warte eine Sekunde, [11 ?!] (http://en.cppreference.com/w/cpp/language/new) [yep] (https://stackoverflow.com/questions/15935080/what-does-new- Auto-Do), hatte ich keine Ahnung. – Yakk

+0

@Yakk Eigentlich bin ich mir ziemlich sicher 11. –

1

Sie können so etwas wie

auto l = new (buf) auto([a, b, c]{}); 

tun, aber Sie müssen wissen, dass buf genügend Platz hat das Lambda zu halten. Um die Größe des tatsächlichen Lambdas finden Sie so etwas wie

auto l = [a, b, c]{}; 
auto size = sizeof(l); 

Aber jetzt haben Sie das Lambda auf dem Stapel erstellt tun müßten, was Sie nicht tun wollten. Aber jetzt wissen Sie, wie groß die Puffer sein muss, und können

auto buf = new char[size]; 
auto lptr = new (buf) auto(std::move(l)); 

tun und das Lambda wird in den Puffer verschoben werden.

Schließlich müssen Sie jedoch das Lambda zerstören und den Puffer freigeben. Der Lambda kann mit einer Template-Funktion leicht zerstört werden.

template<typename Lambda> 
void destruct(Lambda* l) 
{ 
    l->~Lambda(); 
} 
+1

Hier ist eine Möglichkeit, herauszufinden, Größe der Erfassung durch die Verwendung von nicht bewerteten Zweig: Vorlage T * get_ptr (T t) {zurück nullptr; } auto * l = falsch? get_ptr (Lambda geht hier): nullptr; enum {lambda_size = Größe von (* 1); } – rtz

+0

@rtz Das Lambda muss nicht die gleiche Größe wie ein Lambda auf der nächsten Zeile mit dem gleichen Körper haben ... – Yakk

Verwandte Themen