2013-02-13 9 views
16

Der folgende Code wurde mit VC++ 2012 zusammengestellt:Wie lautet die standardmäßige Aufrufkonvention einer C++ - Lambda-Funktion?

void f1(void (__stdcall *)()) 
{} 

void f2(void (__cdecl *)()) 
{} 

void __cdecl h1() 
{} 

void __stdcall h2() 
{} 

int main() 
{ 
    f1(h1); // error C2664 
    f2(h2); // error C2664 

    f1([](){}); // OK 
    f2([](){}); // OK 

    auto fn = [](){}; 

    f1(fn); // OK 
    f2(fn); // OK 
} 

Ich denke, die Fehler sind normal noch die OKs sind abnormal.

Also, meine Fragen sind:

  1. Was ist die Aufrufkonvention eines C++ Lambda-Funktion?

  2. Wie Sie die Aufrufkonvention einer C++ - Lambda-Funktion angeben?

  3. Wenn die Aufrufkonvention nicht definiert ist, wie wird der Stapelspeicherplatz nach dem Aufruf einer Lambda-Funktion korrekt wiederverwendet?

  4. Generiert der Compiler automatisch mehrere Versionen einer Lambda-Funktion? als der folgende Pseudocode:

    [] __stdcall() {};

    [] __cdecl() {}; usw.

+3

diese Frage Verknüpfung scheint nützlich: http://stackoverflow.com/questions/14169295/how-to-specify-vc11-lambda-calling-convention – jogojapan

+0

'f1' wird mit' __stdcall' aber 'h1' wird mit' __cdecl'; wenn du die herum tauscht funktioniert es? – congusbongus

+0

@Cong, Die Fehler sind normal und die OKs sind abnormal. – xmllmx

Antwort

14

auf VC++ 2012 wählen Compiler automatisch Aufruf Konvertierung für staatenlos lambdas (das hat keine Capture-Variablen) wenn Sie "stateless Lambda Funktionszeiger" umwandeln.

MSDN C++11 Features:

Lambdas

[...] Zusätzlich ist in Visual C++ in Visual Studio 2012, staatenlos lambdas umwandelbar sind Zeiger zu funktionieren. [...] (Das Visual C++ in Visual Studio 2012 ist sogar noch besser, weil wir statuslose Lambdas zu Funktionszeigern mit beliebigen Aufrufkonventionen umgewandelt haben. Dies ist wichtig, wenn Sie APIs verwenden, die Dinge wie die Funktion __stdcall erwarten Zeiger)


EDITED.

NB: Die anrufende Umwandlung von C++ Norm ist, hängt es von anderen Spezifikationen wie Plattform ABI (Application binary Interface).

Die folgenden Antworten basieren auf dem Code der Ausgabemontage mit /FAs compiler option. So ist es nur eine Vermutung, und bitte fragen Sie Microsoft für weitere Details; P

Q1. Wie lautet die Aufrufkonvention einer C++ - Lambda-Funktion?

Q3. Wenn die Aufrufkonvention nicht definiert ist, wie wird der Stapelspeicherplatz nach dem Aufruf einer Lambda-Funktion korrekt wiederverwendet?

Zu allererst, C++ Lambda (-Ausdrucks) ist keine Funktion (noch Funktionszeiger), können Sie operator() Lambda Objekt wie ein Aufruf normale Funktion aufrufen. Und Ausgabe Assembly-Code sagt, dass VC++ 2012 Lambda-Körper mit __thiscall aufrufen Konvertierung generiert.

Q2. Wie Sie die Aufrufkonvention einer C++ - Lambda-Funktion angeben?

AFAIK, gibt es keine Möglichkeit. (Es kann nur __thiscall sein)

Q4. Erzeugt der Compiler automatisch mehrere Versionen einer Lambda-Funktion? dh als der folgenden Pseudo-Code: [...]

Wahrscheinlich No. Die VC++ 2012 Lambda-Typ bietet nur eine Lambda-Körper-Implementierung (void operator()()), bietet jedoch mehr „benutzerdefinierte Umwandlung zu funktionieren Zeiger "für jede Aufrufkonvertierung (Funktionszeiger Zeiger zurückgeben mit void (__fastcall*)(void), void (__stdcall*)(void) und void (__cdecl*)(void) Typ).

Hier ist ein Beispiel;

// input source code 
auto lm = [](){ /*lambda-body*/ }; 

// reversed C++ code from VC++2012 output assembly code 
class lambda_UNIQUE_HASH { 
    void __thiscall operator()() { 
    /* lambda-body */ 
    } 
    // user-defined conversions 
    typedef void (__fastcall * fp_fastcall_t)(); 
    typedef void (__stdcall * fp_stdcall_t)(); 
    typedef void (__cdecl * fp_cdecl_t)(); 
    operator fp_fastcall_t() { ... } 
    operator fp_stdcall_t() { ... } 
    operator fp_cdecl_t() { ... } 
}; 
lambda_UNIQUE_HASH lm; 
+2

+1 für die Referenz (obwohl die Antwort nicht alle Aspekte der Frage umfasst). – jogojapan

+5

@jogojapan: Es deckt ziemlich alles ab, da eine Lambda-Funktion immer noch letztlich eine Member-Funktion ist. Und eine Memberfunktion hat keine Aufrufkonventionen. Nicht auf "__cdecl" Weise. Da Aufrufkonventionen ohnehin plattformspezifisch sind, hängt es von jeder Plattform ab, wie sie funktioniert. Microsoft entschied sich für einen * schockierenden * Grad an Kompetenz und entschied sich für den nützlichsten Weg. –

+0

@NicolBolas Was ich meine, ist hauptsächlich, dass der zitierte Text keine klare Antwort auf Teil 2 der Frage zu geben scheint. Die obige Beschreibung besagt, dass Lambdas standardmäßig in alle Aufrufkonventionen umgewandelt werden können, aber das bedeutet nicht unbedingt, dass es keine Möglichkeit gibt, die Konvention zu spezifizieren, wenn man möchte. Es wäre natürlich VC-spezifische Syntax. (Ich bin mir nicht sicher, ob ich Ihren Punkt über Mitgliedsfunktionen verstehe. Lambdas sind immer Mitgliedsfunktionen. Warum ist das so?) – jogojapan

3

Eine zustandslose Lambda-Funktion ist immer noch eine Klasse, aber eine Klasse, die implizit in einen Funktionszeiger umgewandelt werden kann. Der C++ - Standard behandelt keine Aufrufkonventionen, aber es gibt wenig Grund, warum ein statusloses Lambda in keiner Aufrufkonvention einen Wrapper erstellen konnte, der das Stateless Lambda weiterleitet, wenn das Lambda in einen Funktionszeiger konvertiert wird.

Als Beispiel könnten wir dies tun:

#include <iostream> 

void __cdecl h1() {} 
void __stdcall h2(){} 

// I'm lazy: 
typedef decltype(&h1) cdecl_nullary_ptr; 
typedef decltype(&h2) stdcall_nullary_ptr; 

template<typename StatelessNullaryFunctor> 
struct make_cdecl { 
    static void __cdecl do_it() { 
    StatelessNullaryFunctor()(); 
    } 
}; 
template<typename StatelessNullaryFunctor> 
struct make_stdcall { 
    static void __stdcall do_it() { 
    StatelessNullaryFunctor()(); 
    } 
}; 

struct test { 
    void operator()() const { hidden_implementation(); } 

    operator cdecl_nullary_ptr() const { 
    return &make_cdecl<test>::do_it; 
    } 
    operator stdcall_nullary_ptr() const { 
    return &make_stdcall<test>::do_it; 
    } 
}; 

wo unsere test staatenlos nullary Klasse kann implizit in beiden ein cdecl und stdcall Funktionszeiger umgewandelt werden.

Der wichtige Teil davon ist, dass die Aufrufkonvention Teil des Typs des Funktionszeigers ist, so dass operator function_type weiß, welche Aufrufkonvention angefordert wird. Und mit perfekter Weiterleitung kann das Obige sogar effizient sein.

+0

Eine Lambda-Funktion ist eine Klasse? – jogojapan

+4

@jogojapan: Wahrscheinlich sollte es heißen, ein statusloses Lambda ist immer noch ein * Objekt *, aber ein Objekt, das in einen Funktionszeiger umgewandelt werden kann. –

Verwandte Themen