2010-05-29 10 views
15

Gestern habe ich darüber nachgedacht, ob es möglich wäre, den Komfort von C++ 0x Lambda-Funktionen zu verwenden, um Callbacks für Windows-API-Funktionen zu schreiben.Unterstützt C++ 0x __stdcall oder extern "C" capture-nothing lambdas?

Zum Beispiel, was, wenn ich ein Lambda als EnumChildProc mit EnumChildWindows verwenden wollte? Etwas wie:

EnumChildWindows(hTrayWnd, CALLBACK [](HWND hWnd, LPARAM lParam) { 
     // ... 
     return static_cast<BOOL>(TRUE); // continue enumerating 
    }, reinterpret_cast<LPARAM>(&myData)); 

Eine weitere Anwendung wäre extern "C" Rückrufe für C-Routinen zu schreiben. Zum Beispiel:

my_class *pRes = static_cast<my_class*>(bsearch(&key, myClassObjectsArr, myClassObjectsArr_size, sizeof(my_class), extern "C" [](const void *pV1, const void *pV2) { 
     const my_class& o1 = *static_cast<const my_class*>(pV1); 
     const my_class& o2 = *static_cast<const my_class*>(pV2); 

     int res; 
     // ... 
     return res; 
    })); 

Ist das möglich?

Ich kann verstehen, dass Lambdas, die Variablen erfassen, nie mit C kompatibel sein werden, aber es scheint zumindest möglich, dass capture-nothing lambdas kompatibel sein kann.

+0

Dies wurde in einem Fehlerbericht gegen den C++ 11-Standard geklärt, wie ich in meiner Antwort unten erläutere. –

Antwort

14

Lambdas ohne Capture are implicitly convertible zu einem Zeiger auf Funktion (durch eine nicht-explizite Konvertierungsfunktion durch den Typ Schließung definiert).

Die FCD scheint nicht anzugeben, welche Sprachverknüpfung der Funktionstyp des Funktionszeigertyps hat. Wenn Sie also diesen Funktionszeiger an C-Funktionen übergeben müssen, muss die Aufrufkonvention von C++ - Funktionen und C-Funktionen lauten gleich. Ich glaube, dass das unter Windows so ist. So sollten Sie in der Lage sein, die Lambda-Windows-API-Funktionen bei

typedef void(*callbackType)(void *userData); 
extern "C" void someCFunction(callbackType callback); 

int main() { 
    someCFunction([](void *userData) { /* ... */ }); 
} 

FCD Wortlaut 5.1.2/6 weitergeben müssen:

Die Verschlusstyp für eine Lambda-Ausdruck ohne Lambda-Capture verfügt über eine öffentliche nicht-virtuelle nicht-explizite const-Konvertierungsfunktion in Zeiger auf Funktion mit denselben Parameter- und Rückgabetypen wie der Funktionsaufrufoperator des Schließungstyps. Der Wert, der von dieser Konvertierungsfunktion zurückgegeben wird, ist die Adresse einer Funktion, die, wenn sie aufgerufen wird, den gleichen Effekt hat wie das Aufrufen des Funktionsaufrufoperators des Schließungstyps.

Ich denke, die letzte Norm eine Notiz haben sollte, die besagt, dass es eine Konvertierungsfunktion für beide C-Bindung Funktionszeiger und C++ Verknüpfung Funktionszeiger, als Konvertibilität C Funktionszeiger ist ein Ziel dieser Funktionalität ist.

2

Es gibt keinen besonders guten Grund, dass dies nicht auf das Erfassen von Lambdas ausgedehnt werden sollte. Es erfordert einige dynamische Code-Generierung, aber es sollte nicht über den Verstand der Compiler-Autoren, und es würde Interop mit alten C-APIs Größenordnungen einfacher machen - keine Notwendigkeit, Parameter durch untypisierte void * s übergeben (die nicht alle APIs bieten sogar an).

+1

Das Problem ist nicht dynamische Code-Generierung, es ist dynamische Code-Bereinigung. Um diesen Code zu bereinigen, müssten Sie über GC auf Sprachebene verfügen. – Puppy

+2

Nicht mehr als alles andere in C++. Ein shared_ptr zu einem Thunk würde ausreichen. – DrPizza

1

Die Sprache Verknüpfung der Funktionszeiger ergibt sich aus einer Umwandlung eines Capture-weniger Lambda nicht in der C++ 11-Standard spezifiziert wurde, sondern in defect report 1557 gerichtet, die sagt:

5.1.2 [ausdr .prim.lambda] In Absatz 6 wird die Sprachverknüpfung des Funktionstyps der Konvertierungsfunktion des Schließungstyps nicht angegeben.

und die Auflösung war, dass die Sprache Verknüpfung sollte C++ sein:

Die Verschlusstyp für eine Lambda-Ausdruck ohne Lambda-Capture verfügt über eine öffentliche nicht-virtuelle nicht-explizite const Konvertierungsfunktion zu Zeiger auf Funktion mit C++ - Sprachverknüpfung (7.5 [dcl.link]). mit den gleichen Parametern und Rückgabetypen wie der Funktionsaufrufoperator des Schließungstyps. Der zurückgegebene Wert ...

wir diese Sprache in dem Entwurf C++ 14-Standard finden, da der Status DRWP ist es wie scheint dies zu C++ 11 nicht gilt.

+0

Toller Fund. Aber wenn es ein Defekt war, der während der Lebensdauer von C++ 11 ausgelöst und akzeptiert wurde, bedeutet das sicher, dass es definitiv ** auf C++ 11 zutrifft? –

+0

@underscore_d, wenn wir uns die [aktuelle aktive Problemliste] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html) ansehen, die die Begriffe wie 'DRWP' definiert "Ein DR-Problem, dessen Lösung sich im aktuellen Arbeitspapier widerspiegelt. Das Working Paper ist ein Entwurf für eine zukünftige Version des Standards, die meines Wissens nach für C++ 14 und nicht für C++ 11 gilt. –