Function Signature-Like Expressions as C++ Template Arguments

Function signature-like expressions as C++ template arguments

With regards to your first question - about the type int(char, float) - this is a valid C++ type and is the type of a function that takes in a char and a float and returns an int. Note that this is the type of the actual function, not a function pointer, which would be an int (*) (char, float). The actual type of any function is this unusual type. For example, the type of

void DoSomething() {
/* ... */
}

is void ().

The reason that this doesn't come up much during routine programming is that in most circumstances you can't declare variables of this type. For example, this code is illegal:

void MyFunction() { 
void function() = DoSomething; // Error!
}

However, one case where you do actually see function types used is for passing function pointers around:

void MyFunction(void FunctionArgument()) {
/* ... */
}

It's more common to see this sort of function written to take in a function pointer, but it's perfectly fine to take in the function itself. It gets casted behind-the-scenes.

As for your second question, why it's illegal to have the same template written with different numbers of arguments, I don't know the exactly wording in the spec that prohibits it, but it has something to do with the fact that once you've declared a class template, you can't change the number of arguments to it. However, you can provide a partial specialization over that template that has a different number of arguments, provided of course that the partial specialization only specializes over the original number of arguments. For example:

template <typename T> class Function;
template <typename Arg, typename Ret> class Function<Ret (Arg)> {
/* ... */
};

Here, Function always takes one parameter. The template specialization takes in two arguments, but the specialization is still only over one type (specifically, Ret (Arg)).

Function signature as a function template parameter

You cannot partially specialize a function template, that is not supported by the language. What you can do is to create a partially specialized class template with a static member function, and possibly a trampoline function that would instantiate that class template and invoke the static function.

Something like this:

namespace detail
{
template<typename Signature>
struct helper;

template<typename R, typename A1>
struct helper<R(A1)>
{
static void call()
{
// Do stuff with R and A1...
}
};
}

template<typename Signature>
void Func()
{
detail::helper<Signature>::call();
}

Is there a way to pass templated function signature as a template template parameter

In the example below one has a template template parameter that accepts the preferred signature for the function.

Because of the specialization and the lack of a body for the template class, only types for callables are accepted.

It is a generalization of what the OP actually asked:

#include<cassert>

template<typename F>
struct S;

template<typename R, typename... Args>
struct S<R(Args...)> {
using type = R(*)(Args...);
};

template<template<typename> class F>
struct T {
typename F<void(int)>::type ft;
typename F<double(double, double)>::type gt;
};

void f(int) { }
double g(double x, double y) { return x+y; }

int main() {
T<S> t;
t.ft = f;
t.gt = g;
t.ft(42);
auto v = t.gt(1., 1.);
assert(v == 2.);
}

Function signatures in C++ templates

The template parameter to std::function<Signature> is simply the type of a function, i.e., its signature. It uses the same notation as any function declaration except that it isn't named and the name is left out. You may have come across function pointers which use the same notation but the function signature is used for a pointer.

The reason std::function<Signature> (and apparently delegate<Signature>) are implemented using template specialization is to yield a nicer type:

template <typename T> class function;
template <typename R, typename... Args>
class function {
public:
R operator()(Args...);
// ...
};

template <typename R, typename... Args>
class other {
public:
R operator()(Args...);
// ...
};

int main() {
function<int(double, char)> f;
other<int, double, char> o;
}

Since the primary template for function<T> takes one type as argument, using the specialization the argument can be a normal function type. On the other hand, the same isn't done for other<T...> which, thus, gets a list of types.

It is worth nothing that std::function<T> objects can be passed around quite easily without any need to deal with many template arguments: since the function's signature is just a type, this class template takes just one template argument.

How to declare concept function signature with a template type?

The shown concept definition works, except that you need to tell the compiler that SomeFunction is a template:

template<typename SomeTypeT, typename U>
concept SomeType = requires(SomeTypeT s) {
{ s.template SomeFunction<U>() };
};

This is always necessary if you want to reference a template member of a dependent name in a template definition, not specific to concepts. When parsing the definition, the compiler doesn't know yet what type s is and so can't know that SomeFunction is supposed to be a template. But it needs to know, since the meaning of the angle brackets and the whole expression can dependent on it.

Recreate function signature and call via template packs in C++

Well, if they're just C functions, why not overload on the function pointer type?

std::function<void(std::array<int, 5>)> addfunc(void (*f)(int)) {
return [f](std::array<int, 5> const& a) { f(a[0]); };
}

std::function<void(std::array<int, 5>)> addfunc(void (*f)(int,int)) {
return [f](std::array<int, 5> const& a) { f(a[0], a[1]); };
}

// repeat for all necessary arities

Then create std::vector<std::function<void(std::array<int, 5>)>> and push back all your functions. It's easy, doesn't require any templates and will work reasonably well. It introduces the overhead of std::function, though.

You could get rid of that by introducing your own callable type (n of them), that would correspond to the overloads above, provide an operator() and store appropriate function type inside.

Live example.

Function signature as template parameter

template <class Ty>
class Test; /* not defined */
template <class Ret, class Arg0>
class Test<Ret(Arg0)> { /* whatever */ }
template <class Ret, class Arg0, class Arg1>
class Test<Ret(Arg0, Arg1)> { /* whatever */ }
template <class Ret, class Arg0, class Arg1, class Arg2>
class Test<Ret(Arg0, Arg1, Arg2)> { /* whatever */ }

Continue the tedious repetition until you have enough arguments for your needs. In TR1 it was recommended that the various function object templates be able to handle 10 arguments. This was typically implemented with fairly sophisticated macros to simplify coding, but it can be done by brute force.

Why C++ function template contains return type in its signature

Function template overloading can meaningfully depend on the return type:

template<class T> T get_pi() {return 3;}
template<class T>
std::enable_if<std::is_floating_point_v<T>,T> get_pi() {return 3.2;} // closer

It is of course possible to call the float specialization of the first function template, if only by putting a call to it between the two declarations, so those specializations must have different mangled names.

This is actually true of ordinary functions as well. If you could declare

int f();
void f();

It would be possible to select between them in certain cases:

int x=static_cast<int(*)()>(f)();
void g() {
void f(); // redeclare the function
f();
}

However, such tricks notwithstanding, many implementations do omit the return type when mangling a non-template function name, so the standard forbids such overloading (with no diagnostic required if in different translation units).

C++ 17: Deduce signature from Callable in a template

std::function can be constructed from any callable, and can deduce the signature (since c++17). We can use this to make a type trait that extracts the signature.

#include <functional>

template <typename T>
struct get_signature;

template <typename R, typename... Args>
struct get_signature<std::function<R(Args...)>> {
using type = R(Args...);
};

template <typename Callable>
auto deduceSignature(Callable&& c)
{
using Signature = typename get_signature<decltype(std::function{c})>::type;
}

int main() {
deduceSignature([](int a){return 5;});
}

Using variadic template arguments to resolve a lambda signature

Summing up and extending from the comments:

Per [expr.prim.lambda]/3, the type of a lambda-expression is a class type, just like "ordinary, named function object types":

The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type [...]

Further down, /5 specifies:

The closure type for a lambda-expression has a public inline function call operator (13.5.4) whose parameters and return type are described by the lambda-expression’s parameter-declaration-clause and trailing-return-type respectively. This function call operator is declared const (9.3.1) if and only if the lambda-expression’s parameter-declaration-clause is not followed by mutable. It is neither virtual nor declared
volatile. [...]

(it then continues by specifying attributes and exception-specifications)

Which means that the lambda [](int p){ return p/2.0; } behaves in this regard exactly like

struct named_function_object
{
double operator() (int p) const { return p/2.0; }
};

Therefore, your first specialization

template<typename R, typename...As>
struct invokable_type<R(As...)>;

should already be able to deal with lambdas. The SSCCE

#include <utility>

template<class T>
struct decompose;

template<class Ret, class T, class... Args>
struct decompose<Ret(T::*)(Args...) const>
{
constexpr static int n = sizeof...(Args);
};

template<class T>
int deduce(T t)
{
return decompose<decltype(&T::operator())>::n;
}

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

#include <iostream>
int main()
{
std::cout << deduce(test{}) << std::endl;
std::cout << deduce([](int){}) << std::endl;
}

compiles fine on recent versions of clang++ and g++. It seems the problem is related to g++4.7


Further research shows that g++-4.7.3 compiles the above example.

The problem might be related to the misconception that a lambda-expression would yield a function type. If we define do_something as

template<class C>
void do_something(C&&)
{
std::cout << invokable_type<C>::n << std::endl;
}

Then for a call like do_something( [](int){} ), the template parameter C will be deduced to the closure type (no reference), i.e. a class type. The analogous case for the struct test defined above, would be do_something( test{} ), in which case C would be deduced to test.

The specialization of invokable_type that is instantiated is therefore the general case

template<class T>
struct invokable_type;

as T in both cases is not a "composite type" like a pointer or function type. This general case can be used by assuming it only takes a pure class type, and then using the member T::operator() of that class type:

template<class T>
struct invokable_type
{
constexpr static int n = invokable_type<&T::operator()>::n;
};

or, as Potatoswatter put it, via inheritance

template<class T>
struct invokable_type
: invokable_type<&T::operator()>
{};

Potatoswatter's version however is more general and probably better, relying on a SFINAE check for the existance of T::operator(), which can provide a better diagnostic message if the operator cannot be found.

N.B. If you prefix a lambda-expression that doesn't capture anything with a unary +, it'll be converted to a pointer-to-function. do_something( +[](int){} ) will work with a specialization invokable_type<Return(*)(Args...)>.



Related Topics



Leave a reply



Submit