Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a function template that takes a functor with variable arguments

I'm trying to implement a function template (in C++11) whose parameter is a lambda with arbitrary parameters, and return a compatible std::function object. The goal is for the returned function when called to invoke the original lambda asynchronously, but for now I'm just returning the original lambda.

The problem is simply getting the compiler to accept a lambda as the parameter of the function template. Here are some simple templates:

#include <functional>
using namespace std;

template <class Arg>
function<void(Arg)> pass1(function<void(Arg)> fn) {
    return fn;
}

template <class... Args>
function<void(Args...)> passn(function<void(Args...)> fn) {
    return fn;
}

They do the same thing, it's just that pass1 only works on single-parameter functors while passn takes an arbitrary number.

So now we try using them, first pass1:

    auto p1 = pass1( [](int a)->void {cout << a;} );  // ERROR

This doesn't work; the compiler can't seem to tell what parameters the lambda takes. Clang error message is:

Untitled.cpp:17:12: error: no matching function for call to 'pass1'
    auto p1 = pass1( [](int a)->void {cout << a;} );
              ^~~~~
Untitled.cpp:6:21: note: candidate template ignored: could not match 'function<void (type-parameter-0-0)>' against '(lambda at Untitled.cpp:17:19)'
function<void(Arg)> pass1(function<void(Arg)> fn) {

I can work around this by explicitly specifying the template parameter type:

    auto p2 = pass1<int>( [](int a)->void {cout << a;} );  // OK

However, this workaround fails with passn:

    auto p3 = passn<int>( [](int a)->void {cout << a;} );

Untitled.cpp:23:12: error: no matching function for call to 'passn'
    auto p3 = passn<int>( [](int a)->void {cout << a;} );
              ^~~~~~~~~~
Untitled.cpp:11:25: note: candidate template ignored: could not match 'function<void (int, type-parameter-0-0...)>' against '(lambda at Untitled.cpp:23:24)'
function<void(Args...)> passn(function<void(Args...)> fn) {
                    ^

The weird thing is that I can invoke passn if I pass it a function object:

    function<void(int)> fn = [](int a)->void {cout << a;};
    auto x = passn<int>(fn);  // OK

...in fact, I don't even have to specify the template parameter type:

    auto y = passn(fn);  // OK

The function I actually need is going to be like passn, but I don't want the extra verbiage of having to wrap a function object around the lambda every time I call it. Am I missing something, or is this just not possible? Would it be possible in C++14?

like image 547
Jens Alfke Avatar asked Feb 08 '17 22:02

Jens Alfke


People also ask

What happens when an argument is passed to a function template?

When an argument of a data type is passed to functionName (), the compiler generates a new version of functionName () for the given data type. Once we've declared and defined a function template, we can call it in other functions or templates (such as the main () function) with the following syntax

How do you write a template for a function?

Defining a Function Template A function template starts with the keyword template followed by template parameter (s) inside <> which is followed by the function definition. template <typename T> T functionName(T parameter1, T parameter2,...) { // code }

What is a variadic function template in JavaScript?

So basically, Variadic function templates are functions that can take multiple numbers of arguments. template (typename arg, typename... args) return_type function_name (arg var1, args... var2) Note: The arguments must be put inside angular brackets.

What are some examples of functions that use variable argument lists?

The family of functions is an example of functions that use variable argument lists. printf argument-declaration-list To access arguments after those declared, use the macros contained in the standard include file <stdarg.h> as described below.


1 Answers

You can use this implementation of passn:

#include <functional>
#include <iostream>

template <class RetVal, class T, class... Args>
std::function<RetVal(Args...)> get_fun_type(RetVal (T::*)(Args...) const);

template <class RetVal, class T, class... Args>
std::function<RetVal(Args...)> get_fun_type(RetVal (T::*)(Args...));

template <class T>
auto passn(T t) -> decltype(get_fun_type(&T::operator())) {
    return t;
}

int main() {
    auto fun = passn([](int a) { std::cout << a; });
    fun(42);
}

(demo)

It assumes you pass in a type that has an operator(). It takes the address of that function and deduces parameters from that member pointer.

The function will fail if you pass it an object that has multiple operator()s because then taking its address will be ambiguous, but lambdas will not produce that problem.

like image 68
nwp Avatar answered Sep 20 '22 22:09

nwp