17

The following code was compiled with VC++ 2012:

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
}

I think the errors are normal yet the OKs are abnormal.

So, my questions are:

  1. What's the calling convention of a C++ lambda function?

  2. How to specify the calling convention of a C++ lambda function?

  3. If the calling convention is not defined, how to correctly recycle the stack space after having called a lambda function?

  4. Does the compiler automatically generate multiple versions of a lambda function? i.e. as the following pseudo-code:

    [] __stdcall (){};

    [] __cdecl (){}; etc.

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • 3
    Linking this question seems useful: http://stackoverflow.com/questions/14169295/how-to-specify-vc11-lambda-calling-convention – jogojapan Feb 13 '13 at 03:20
  • `f1` is using `__stdcall` but `h1` is using `__cdecl`; if you swap those around does it work? – congusbongus Feb 13 '13 at 03:24
  • @Cong, The errors are normal and the OKs are abnormal. – xmllmx Feb 13 '13 at 03:25
  • Wouldn't it be better to describe these functions as `f1(const std::function)`? – tadman Feb 13 '13 at 03:31
  • @jogojapan, What I mean is that question's focus is different from this one. As for the second question, Yes. You're right. – xmllmx Feb 13 '13 at 03:33
  • 5
    Since the C++ standard doesn't mention anything about calling conventions, the answer to your first question would seem to become compiler specific. – Jesse Good Feb 13 '13 at 03:33
  • @tadman, I wonder why OKs rather than how to make the code work. – xmllmx Feb 13 '13 at 03:34
  • 3
    I think the question linked by @jogojapan answers this question. Looking at the symbol dump posted in that one it seems VC generates function call operators with all 3 calling conventions when you define a lambda. It'll link the most appropriate match based on the how the lambda is being called. – Praetorian Feb 13 '13 at 03:35
  • @Jesse, if the compiler doesn't know the correct calling convention, the program can crash or leak memory at run-time. – xmllmx Feb 13 '13 at 03:35
  • 1
    @jogojapan, I'm sorry for my overlook of the link you provided. I have upvoted your comment and deleted my first comment. – xmllmx Feb 13 '13 at 03:45
  • Is it possible in C++11 to have a lambda function (stateless or otherwise) with external linkage? That is the only situation I can think of where the compiler might not be able to automatically do the Right Thing at each callsite. – zwol Feb 13 '13 at 03:47

2 Answers2

16

On VC++ 2012, compiler choose automatically calling conversion for stateless lambdas (that has no capture variables) when you convert "stateless lambda to function pointer".

MSDN C++11 Features:

Lambdas

[...] Additionally in Visual C++ in Visual Studio 2012, stateless lambdas are convertible to function pointers. [...] (The Visual C++ in Visual Studio 2012 is even better than that, because we've made stateless lambdas convertible to function pointers that have arbitrary calling conventions. This is important when you are using APIs that expect things like __stdcall function pointers.)


EDITED:

NB: The calling conversion is out of C++ Standard, it depends on other specification such as platform ABI(application binary interface).

The following answers are based on output assembly code with /FAs compiler option. So it's a mere guess, and please ask Microsoft for more detail ;P

Q1. What's the calling convention of a C++ lambda function?

Q3. If the calling convention is not defined, how to correctly recycle the stack space after having called a lambda function?

First of all, C++ lambda(-expression) is NOT a function (nor function pointer), you can call operator() to lambda object like a calling normal function. And output assembly code says that VC++ 2012 generates lambda-body with __thiscall calling conversion.

Q2. How to specify the calling convention of a C++ lambda function?

AFAIK, there is no way. (It may be only __thiscall)

Q4. Does the compiler automatically generate multiple versions of a lambda function? i.e. as the following pseudo-code: [...]

Probably No. The VC++ 2012 lambda-type provides only one lambda-body implementation (void operator()()), but provides multiple "user-defined conversion to function pointer" for each calling conversion (operator return function pointer with void (__fastcall*)(void), void (__stdcall*)(void), and void (__cdecl*)(void) type).

Here is an example;

// 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;
Community
  • 1
  • 1
yohjp
  • 2,985
  • 3
  • 30
  • 100
  • 2
    +1 for the reference (although the answer doesn't cover all aspects of the question). – jogojapan Feb 13 '13 at 03:44
  • 5
    @jogojapan: It pretty much covers everything, since a lambda function is still ultimately a member function. And a member function doesn't really have calling conventions. Not in the `__cdecl` way. Since calling conventions are platform-specific anyway, it's up to each platform to decide how it works. Microsoft, displaying a *shocking* degree of competence, decided on the most useful way. – Nicol Bolas Feb 13 '13 at 04:02
  • @NicolBolas What I mean is mainly that the quoted text doesn't seem to provide a clear answer to part 2 of the question. The description above says that lambdas are by default convertible to all calling conventions, but that doesn't necessarily imply that there is no way to specify the convention if one wanted to. It would be VC-specific syntax, of course. (I am not sure if I understand your point about member functions. Lambdas are always member functions.. why is that?) – jogojapan Feb 13 '13 at 04:15
  • 1
    @jogojapan: "*Lambdas are always member functions.. why is that?*" Because lambda functions in C++ are *functors*; they're objects. They have an `operator()` which is where your function goes. Even capture-less lambdas are objects. The are *convertible* to function pointers, but they aren't function pointers. They're objects. Except for the convertibility, a lambda is no different functionally from `struct Nameless {Ret operator()(...) {...}};`. Indeed, the standard requires that it is implemented this way, with a typename and everything. – Nicol Bolas Feb 13 '13 at 04:19
  • @NicolBolas Ok, right. And being (called in the same way as) a member function implies that the concept of different calling conventions doesn't quite apply... your explanation should be part of the answer. – jogojapan Feb 13 '13 at 04:34
  • So, there are some interesting quirks in stateless lambdas in VS2012: basically they are overly eager to turn themselves into function pointers with some arbitrary calling convention. – Yakk - Adam Nevraumont Jul 05 '13 at 03:14
  • 1
    Also see [this reference](https://blogs.msdn.microsoft.com/oldnewthing/20150220-00/?p=44623/) – Shafik Yaghmour Nov 16 '16 at 22:02
3

A stateless lambda function is still a class, but a class that can be implicitly converted into a function pointer.

The C++ standard doesn't cover calling conventions, but there is little reason why a stateless lambda could not create a wrapper in any calling convention that forwards through to the stateless lambda when the lambda is converted to a function pointer.

As an example, we could do this:

#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;
  }
};

where our test stateless nullary class can be converted into both a cdecl and stdcall function pointer implicitly.

The important part of this is that the calling convention is part of the type of the function pointer, so operator function_type knows what calling convention is being requested. And with perfect forwarding, the above can even be efficient.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524