Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to capture a variable number of parameters in a lambda?

Tags:

c++

gcc

lambda

Consider the following set of examples.

  1. The function takeOnlyVoidFunction takes a function with zero arguments and simply executes it.
  2. The function takeVariableArguments takes a variable number of arguments and executes the function using the arguments.
  3. The function captureVariableArgs attempts to convert the second function into a lambda form that is acceptable by the first function, but it does not compile.

How can I make the function captureVariableArgs compile and exhibit the correct behavior of converting a function with a variable number of arguments into a closure with no arguments?

#include <stdio.h>
#include <functional>

void takeOnlyVoidFunction(std::function<void()> task) {
    task();
}

template<typename _Callable, typename... _Args>
    void takeVariableArguments(_Callable&& __f, _Args&&... __args) {
     __f(__args...);
}

// How can I make this function compile?
template<typename _Callable, typename... _Args>
    void captureVariableArgs(_Callable&& __f, _Args&&... __args) {
    takeOnlyVoidFunction([=]() { __f(__args...);});
}

void normalFunction(int a, int b) {
    printf("I am a normal function which takes params (%d,%d)\n", a, b);
}

int main() {
    int a = 7;
    int b = 8;
    takeVariableArguments(normalFunction, a, b);
    takeOnlyVoidFunction([=](){ normalFunction(a,b);});
    captureVariableArgs(normalFunction, a, b);
}

I'm running gcc 4.9.2. Here is the compiler error I see.

g++ -std=c++11    Test.cc   -o Test
Test.cc: In instantiation of ‘captureVariableArgs(_Callable&&, _Args&& ...)::<lambda()> [with _Callable = void (&)(int, int); _Args = {int&, int&}]’:
Test.cc:16:38:   required from ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’
Test.cc:16:50:   required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’
Test.cc:28:45:   required from here
Test.cc:16:34: error: variable ‘__f’ has function type
     takeOnlyVoidFunction([=]() { __f(__args...);});
                                  ^
Test.cc:16:34: error: variable ‘__f’ has function type
Test.cc: In instantiation of ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’:
Test.cc:16:50:   required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’
Test.cc:28:45:   required from here
Test.cc:16:34: error: field ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>::<__f capture>’ invalidly declared function type
In file included from Test.cc:2:0:
/usr/include/c++/4.9/functional:2418:7: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>; <template-parameter-2-2> = void; _Res = void; _ArgTypes = {}]’, declared using local type ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’, is used but never defined [-fpermissive]
       function<_Res(_ArgTypes...)>::
       ^

Update: A more minimal example demonstrating this problem.

#include <stdio.h>

// How can I make this function compile?
template<typename _Callable>
void captureVariableArgs(_Callable&& __f) {
    takeOnlyVoidFunction( [=]{ __f(); } );
}

void normalFunction() {
    printf("I am a normal function\n");
}

int main(){
    captureVariableArgs(normalFunction);
}
like image 350
merlin2011 Avatar asked Feb 18 '16 06:02

merlin2011


People also ask

How do you capture variables in lambda?

Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.

Can a function have variable number of parameters?

To call a function with a variable number of arguments, simply specify any number of arguments in the function call. An example is the printf function from the C run-time library. The function call must include one argument for each type name declared in the parameter list or the list of argument types.

Can lambda have multiple parameters?

A lambda function can have any number of parameters, but the function body can only contain one expression.

What are captures in lambda?

Capture clauseA lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.


1 Answers

As another potential workaround for GCC, instead of using a lambda, you could use std::bind:

template <typename F, typename... Args>
auto captureVariable(F&& f, Args&&... args)
{
    return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}

This works for me under GCC 4.9.3.

like image 98
Yuushi Avatar answered Oct 19 '22 15:10

Yuushi