How to Specify Vc11 Lambda Calling Convention

how to specify vc11 lambda calling convention

Cast it:

WinApiFunc(static_cast<void(__stdcall *)()>(func));

Or store it into a local variable first:

void (__stdcall *funcp)() = func;
WinApiFunc(funcp);

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

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;

Hot to handle c++11 lambda with both this pointer captured and calling convention specified

Only lambdas that do not capture anything can be converted to function pointers and this API only accepts pointers.

What you can do is to keep the lambda stateless, and transfer this through lparam:

EnumWindows(
[](HWND, LPARAM lparam){
return reinterpret_cast<Your_Class*>(lparam)->my_class_member_function();
},
reinterpret_cast<LPARAM>(this));

how to use __stdcall to qualify C++ lambda?

I just noticed you have the visual studio 2010 tag. Stateless lambdas were implemented in VC11. Reference:

After lambdas were voted into the Working Paper (v0.9) and mutable
lambdas were added (v1.0), the Standardization Committee overhauled
the wording, producing lambdas v1.1. This happened too late for us
to implement in VC10, but we've already implemented it in VC11.
The
lambdas v1.1 wording clarifies what should happen in corner cases like
referring to static members, or nested lambdas. This fixes a bunch of
bugs triggered by complicated lambdas. Additionally, stateless
lambdas are now convertible to function pointers in VC11. This isn't
in N2927's wording, but I count it as part of lambdas v1.1 anyways.
It's FDIS 5.1.2 [expr.prim.lambda]/6: "The closure type for a
lambda-expression with no lambda-capture has a public non-virtual
non-explicit const conversion function to pointer to function having
the same parameter and return types as the closure type’s function
call operator.
The value returned by this conversion function shall be
the address of a function that, when invoked, has the same effect as
invoking the closure type’s function call operator." (It's even
better than that, since we've made stateless lambdas convertible to
function pointers with arbitrary calling conventions. This is
important when dealing with APIs that expect __stdcall function
pointers and so forth.
)

Also, note that this conversion happens when there is no capture specification as mentioned in the second bolded quote.

Changing Calling Convention

You can make a wrapper for translation between different calling conventions:

template<typename Func, Func* callback>
auto make_callback()
{
return &detail::callback_maker<Func, callback>::call;
}

with callback_maker defined as

template<typename T, T*>
struct callback_maker;

template<typename R, typename... Params, R(*Func)(Params...)>
struct callback_maker<R(Params...), Func>
{
static R __stdcall call(Params... ps)
{
return Func(std::forward<Params>(ps)...);
}
};

This is aimed to be a fairly general solution, allowing you to specify the function prototype. You can use it as follows:

//  external_api(¬_stdcall_func); // error
external_api(make_callback<void(int,int), ¬_stdcall_func>());

demo


If the pointer is to be determined at runtime, you could keep the callback in the user data. You'd have to manage the lifetime of that correctly, but it's likely that you already need to to that. Again, attempting a generic solution. Make a callback and tell it which argument is the user data pointer:

template<typename Callback, size_t N>
auto make_callback()
{
using callback_maker = detail::callback_maker<Callback, N>;
return &callback_maker::call;
}

With callback_maker defined as

template<typename T, size_t N>
struct callback_maker;

template<typename R, typename... Params, size_t N>
struct callback_maker<R(*)(Params...), N>
{
using function_type = R(Params...);

static R __stdcall call(Params... ps)
{
void const* userData = get_nth_element<N>(ps...);
auto p = static_cast<pair<function_type*, void*> const*>(userData);
return p->first(ps...);
}
};

and get_nth_element as

template<size_t N, typename First, typename... Ts>
decltype(auto) get_nth_element_impl(false_type, First&& f, Ts&&...);

template<size_t N, typename First, typename... Ts>
decltype(auto) get_nth_element_impl(true_type, First&&, Ts&&... ts)
{
return get_nth_element_impl<N-1>(integral_constant<bool, (N > 1)>{}, forward<Ts>(ts)...);
}

template<size_t N, typename First, typename... Ts>
decltype(auto) get_nth_element_impl(false_type, First&& f, Ts&&...)
{
return forward<First>(f);
}

template<size_t N, typename... Ts>
decltype(auto) get_nth_element(Ts&&... ts)
{
return get_nth_element_impl<N>(integral_constant<bool, (N > 0)>{}, forward<Ts>(ts)...);
}

Now, on the call site

using callback_t = CTMuint(*)(const void *aBuf, CTMuint aCount, void *aUserData);
auto runtime_ptr = ¬_stdcall_func;

pair<callback_t, void*> data;
data.first = runtime_ptr;
data.second = nullptr; // actual user data you wanted

auto callback = make_callback<callback_t, 2>();

ctmSaveCustom({}, callback, &data, nullptr);

demo


As per Andrey Turkin's suggestion, you can replace the user data pointer in the parameter list. Along with forward_as_tuple, it obviates the need for get_nth_element. The upgraded call function:

static R __stdcall call(Params... ps)
{
auto params_tuple = forward_as_tuple(ps...);
void const* userData = get<N>(params_tuple);
auto p = static_cast<pair<function_type*, void*> const*>(userData);
get<N>(params_tuple) = p->second;
return apply(p->first, move(params_tuple));
}

and here's a simplistic implementation of C++17's apply:

template<typename Func, typename T, size_t... Is>
decltype(auto) apply_impl(Func f, T&& t, index_sequence<Is...>)
{
return f(get<Is>(t)...);
}

template<typename Func, typename... Ts>
decltype(auto) apply(Func f, tuple<Ts...>&& tup)
{
return apply_impl(f, move(tup), index_sequence_for<Ts...>{});
}

demo

Creating thread with _beginthreadex, __stdcall and a lambda

This actually almost works as-is. Lambdas with no captures can convert to function pointers so they are, to a degree, compatible with C-like APIs.

However, _beginthreadex is expecting a function pointer to a __stdcall function; the "native" function pointer is this; the converted lambda is not.

But Microsoft have kindly made it so that it can be! All you need is a cast to "coerce" the function pointer. This is a little unintuitive because your lambda is already __stdcall (that's what WINAPI expands to), but whatever.

So:

auto lambda = [](void* data) WINAPI -> unsigned int
{
return (napolniDrevo() ? 0 : 1);
};

HANDLE m_hThread = (HANDLE)_beginthreadex(
0, 0,
static_cast<unsigned int(WINAPI*)(void*)>(lambda),
0, 0, 0
);

But actually I'd recommend std::thread instead for clean, portable code!

Nested lambda expressions are very slow to compile and generate huge object file by Visual C++

Not sure how useful such deeply nested lambdas are but for what it is worth as far as I can tell this is a bug, the Visual Studio compiler limits document states (emphasis mine):

The C++ standard recommends limits for various language constructs. The following is a list of constructs where the Visual C++ compiler does not implement the recommended limits. The first number is the recommended limit and the second number is the limit implemented by Visual C++:

and includes the following bullet:

Nesting levels of compound statements, iteration control structures, and selection control structures [256] (256).

If we look at the grammar in the C++ draft standard compound-statement will eventually get back to primary-expression which includes lambda-expression. So Visual Studio should support up to 256 levels of nesting.

You could also see this by looking at the grammar for lambda-expression which is as follows:

lambda-introducer lambda-declaratoropt compound-statement

The draft standard has a set of recommend limits in Annex B but they are only guidelines and do not determine compliance.

Update

The bug report the OP filed was updated recently to indicate this will be fixed in a future release.



Related Topics



Leave a reply



Submit